You are on page 1of 1126

Guide du développeur

Borland ® ™
Delphi 5
pour Windows 98, Windows 95, Windows NT
Reportez-vous au fichier DEPLOY.TXT situé dans le répertoire racine de votre produit Delphi 5 pour obtenir la
liste complète des fichiers que vous pouvez distribuer en accord avec les termes du contrat de licence.
Les applications mentionnées dans ce manuel sont brevetées ou en attente de brevet. Ce document ne donne
aucun droit sur ces brevets.
COPYRIGHT © 1983, 1999 Inprise Corporation. Tous droits réservés. Tous les produits Inprise et Borland sont des
marques ou des marques déposées de Inprise Corporation. Tous les autres noms de produits sont des marques
déposées de leurs fabricants respectifs.
Imprimé en Irlande
HDE1350WW21001 3E2R799
9900010203-9 8 7 6 5 4 3 2 1
D3
Table des matières
Chapitre 1 Génération d’un nouveau gestionnaire
d’événement. . . . . . . . . . . . . . . . . . 2-28
Introduction 1-1 Génération du gestionnaire de l’événe-
Contenu de ce manuel . . . . . . . . . . . . . .1-1 ment par défaut d’un composant. . . . . 2-28
Conventions typographiques. . . . . . . . . . .1-3 Recherche de gestionnaires
d’événements . . . . . . . . . . . . . . . . . 2-29
Support technique Inprise . . . . . . . . . . . .1-3 Association d’un événement à un
gestionnaire d’événement existant . . . . 2-29
Partie I Association d’événements de menu à des
Programmation avec Delphi gestionnaires d’événements . . . . . . . .
Suppression de gestionnaires
2-30

d’événements . . . . . . . . . . . . . . . . . 2-31
Chapitre 2 Emploi d’objets utilitaires . . . . . . . . . . . . 2-31
Utilisation du Pascal Objet Utilisation des listes. . . . . . . . . . . . . . . 2-31
avec la VCL 2-1 Utilisation des listes de chaînes . . . . . . . 2-32
Chargement et enregistrement de listes
Pascal Objet et VCL . . . . . . . . . . . . . . . .2-1 de chaînes . . . . . . . . . . . . . . . . . . . 2-32
Utilisation du modèle objet . . . . . . . . . . .2-2 Création d’une nouvelle liste de chaînes . 2-33
Qu’est-ce qu’un objet ? . . . . . . . . . . . . . 2-2 Manipulation des chaînes d’une liste . . . 2-35
Examen d’un objet Delphi . . . . . . . . . . . 2-3 Association d’objets à une liste de
Héritage des données et du code d’un chaînes . . . . . . . . . . . . . . . . . . . . . 2-37
objet . . . . . . . . . . . . . . . . . . . . . . . . 2-6 Le registre et les fichiers INI Windows . . . 2-37
Objets, composants et contrôles. . . . . . . . 2-6 Utilisation des flux . . . . . . . . . . . . . . . 2-38
Portée et qualificateurs . . . . . . . . . . . . . 2-7 Utilisation de modules de données et de
Déclarations privées, protégées, publiques modules de données distants . . . . . . . . 2-38
et publiées. . . . . . . . . . . . . . . . . . . . 2-8 Création et modification de modules de
Utilisation de variables objet. . . . . . . . . . 2-8 données . . . . . . . . . . . . . . . . . . . . . 2-38
Création, instanciation et destruction Création de règles de gestion dans un
d’objets . . . . . . . . . . . . . . . . . . . . . . 2-9 module de données . . . . . . . . . . . . . 2-39
Composants et appartenance . . . . . . . . 2-10 Accès à un module de données depuis une
Utilisation des composants . . . . . . . . . . . 2-10 fiche . . . . . . . . . . . . . . . . . . . . . . . 2-39
Composants standard de Delphi . . . . . . . 2-11 Ajout d’un module de données distant à
Propriétés communes à tous les un projet serveur d’application . . . . . . . 2-40
composants . . . . . . . . . . . . . . . . . . 2-11 Utilisation du référentiel d’objets . . . . . . . 2-40
Contrôles texte . . . . . . . . . . . . . . . . . 2-13 Partage d’éléments dans un projet. . . . . . 2-40
Contrôles de saisies spécialisées . . . . . . 2-15 Ajout d’éléments au référentiel d’objets . . 2-41
Contrôles bouton et similaires . . . . . . . 2-16 Partage d’objets par une équipe de
Gestion de listes . . . . . . . . . . . . . . . . 2-19 développement. . . . . . . . . . . . . . . . . 2-41
Regroupement de composants . . . . . . . 2-21 Utilisation d’un élément du référentiel
Rétroaction visuelle . . . . . . . . . . . . . . 2-22 d’objets dans un projet . . . . . . . . . . . . 2-41
Grilles . . . . . . . . . . . . . . . . . . . . . . 2-24 Copie d’un élément . . . . . . . . . . . . . . 2-42
Affichage graphique. . . . . . . . . . . . . . 2-25 Héritage d’un élément . . . . . . . . . . . . 2-42
Boîtes de dialogue standard Windows . . 2-26 Utilisation d’un élément . . . . . . . . . . . 2-42
Initialisation des propriétés d’un Utilisation de modèles de projet . . . . . . . 2-42
composant . . . . . . . . . . . . . . . . . . . . 2-26 Modification d’éléments partagés . . . . . . 2-43
Utilisation de l’inspecteur d’objet . . . . . 2-26 Spécification d’un projet par défaut, d’une
Initialisation des propriétés à l’exécution . 2-27 nouvelle fiche et de la fiche principale . . 2-43
Appel de méthodes . . . . . . . . . . . . . . . 2-27 Ajout de composants personnalisés à
Utilisation des événements et des l’EDI . . . . . . . . . . . . . . . . . . . . . . . . 2-43
gestionnaires d’événements . . . . . . . . . 2-28

i
Chapitre 3 Routines de la bibliothèque d’exécution
manipulant des chaînes . . . . . . . . . . . 3-29
Sujets de programmation généraux 3-1 Routines manipulant les caractères
Gestion des exceptions . . . . . . . . . . . . . .3-1 étendus. . . . . . . . . . . . . . . . . . . . . 3-29
Protection des blocs de code. . . . . . . . . . 3-2 Routines usuelles de manipulation des
Réponse aux exceptions . . . . . . . . . . . . 3-2 chaînes longues. . . . . . . . . . . . . . . . 3-30
Exceptions et contrôle d’exécution . . . . . . 3-3 Déclaration et initialisation de chaînes . . . 3-32
Réponses à des exceptions imbriquées . . . 3-3 Mélange et conversion de types chaîne. . . 3-33
Protection de l’allocation de ressources . . . 3-4 Conversions de chaînes en PChar . . . . . . 3-33
Quelles ressources doivent être protégées ? . 3-4 Dépendances de chaîne. . . . . . . . . . . . 3-34
Création d’un bloc de protection de Renvoi d’une variable locale PChar . . . . 3-34
ressource. . . . . . . . . . . . . . . . . . . . . 3-5 Transfert d’une variable locale comme
Gestion des exceptions RTL . . . . . . . . . . 3-6 PChar. . . . . . . . . . . . . . . . . . . . . . 3-34
Qu’est-ce qu’une exception RTL ? . . . . . . 3-6 Directives de compilation portant sur les
Création d’un gestionnaire d’exception . . . 3-7 chaînes. . . . . . . . . . . . . . . . . . . . . . 3-35
Instructions de gestion des exceptions . . . 3-8 Chaînes et caractères : sujets apparentés . . 3-36
Utilisation de l’instance d’exception . . . . . 3-9 Utilisation des fichiers . . . . . . . . . . . . . . 3-36
Portée des gestionnaires d’exceptions . . . . 3-9 Manipulation de fichiers . . . . . . . . . . . . 3-37
Spécification du gestionnaire d’exception Suppression d’un fichier . . . . . . . . . . . 3-37
par défaut . . . . . . . . . . . . . . . . . . . 3-10 Recherche d’un fichier . . . . . . . . . . . . 3-37
Gestion des classes d’exceptions . . . . . . 3-10 Modifications des attributs d’un fichier . . 3-39
Redéclenchement de l’exception . . . . . . 3-11 Modification d’un nom de fichier . . . . . 3-39
Gestion des exceptions des composants . . . 3-12 Routines date-heure de fichier. . . . . . . . 3-40
Utilisation de Copie d’un fichier . . . . . . . . . . . . . . . 3-40
TApplication.HandleException. . . . . . . . 3-13 Types fichier et E/S de fichier . . . . . . . . 3-40
Exceptions silencieuses . . . . . . . . . . . . . 3-13 Utilisation des flux fichier . . . . . . . . . . . 3-41
Définition d’exceptions personnalisées. . . . 3-14 Création et ouverture de fichiers . . . . . . 3-41
Déclaration d’un type objet exception. . . 3-14 Utilisation du handle de fichier . . . . . . . 3-42
Déclenchement d’une exception . . . . . . 3-15 Lecture et écriture de fichiers . . . . . . . . 3-42
Utilisation des interfaces . . . . . . . . . . . . 3-15 Lecture et écriture de chaînes . . . . . . . . 3-43
Création d’interfaces. . . . . . . . . . . . . . . 3-15 Déplacements dans un fichier . . . . . . . . 3-44
Partage d’interfaces entre des classes . . . 3-16 Position et taille de fichier . . . . . . . . . . 3-44
Utilisation d’interfaces avec des Copie . . . . . . . . . . . . . . . . . . . . . . . 3-45
procédures . . . . . . . . . . . . . . . . . . 3-17 Définition de nouveaux types de données . 3-45
Implémentation de IUnknown . . . . . . . . 3-18
TInterfacedObject. . . . . . . . . . . . . . . . . 3-19 Chapitre 4
Utilisation de l’opérateur as . . . . . . . . . . 3-19 Création d’applications,
Réutilisation de code et délégation . . . . . . 3-20
Utilisation de implements pour la de composants
délégation . . . . . . . . . . . . . . . . . . . 3-20 et de bibliothèques 4-1
Agrégation . . . . . . . . . . . . . . . . . . . 3-21
Gestion mémoire des objets interface . . . . 3-22 Création d’applications . . . . . . . . . . . . . 4-1
Utilisation du comptage de références . . 3-22 Applications Windows . . . . . . . . . . . . . . 4-1
Situations où il ne faut pas utiliser le Modèles d’interfaces utilisateur . . . . . . . . 4-2
comptage de références. . . . . . . . . . . 3-24 Définition des options de l’EDI, du projet
Utilisation d’interfaces dans des et de la compilation . . . . . . . . . . . . . . 4-2
applications distribuées . . . . . . . . . . . . 3-24 Modèles de programmation. . . . . . . . . . . 4-3
Utilisation des chaînes. . . . . . . . . . . . . . 3-25 Applications console . . . . . . . . . . . . . . . 4-3
Types caractère . . . . . . . . . . . . . . . . . . 3-25 Applications service. . . . . . . . . . . . . . . . 4-3
Types chaîne. . . . . . . . . . . . . . . . . . . . 3-26 Threads de service . . . . . . . . . . . . . . . . 4-6
Chaînes courtes . . . . . . . . . . . . . . . . 3-26 Propriétés de nom d’un service. . . . . . . . 4-8
Chaînes longues . . . . . . . . . . . . . . . . 3-27 Débogage des services . . . . . . . . . . . . . 4-8
Chaînes étendues . . . . . . . . . . . . . . . 3-28 Création de paquets et de DLL . . . . . . . . 4-9
Types PChar . . . . . . . . . . . . . . . . . . 3-28 Utilisation des paquets et des DLL . . . . . . 4-9
Chaînes ouvertes. . . . . . . . . . . . . . . . 3-29 Ecriture d'applications de base de données . . 4-10

ii
Conception d’applications distribuées . . . . 4-10 Création et gestion de menus . . . . . . . . . 5-16
Distribution d’applications en utilisant Ouverture du concepteur de menus. . . . . 5-17
TCP / IP . . . . . . . . . . . . . . . . . . . . . 4-11 Conception de menus . . . . . . . . . . . . . 5-19
Utilisation de sockets dans les Nom des menus . . . . . . . . . . . . . . . . 5-19
applications . . . . . . . . . . . . . . . . . . 4-11 Nom des éléments de menu. . . . . . . . . 5-19
Création d’applications serveur Web . . . 4-11 Ajout, insertion et suppression d’éléments
Distribution d’applications en utilisant de menu . . . . . . . . . . . . . . . . . . . . 5-20
COM et DCOM. . . . . . . . . . . . . . . . . 4-12 Création de sous-menus . . . . . . . . . . . 5-22
COM et DCOM . . . . . . . . . . . . . . . . 4-12 Affichage du menu . . . . . . . . . . . . . . 5-24
MTS . . . . . . . . . . . . . . . . . . . . . . . 4-12 Edition des éléments de menu dans
Distribution d’applications en utilisant l’inspecteur d’objets . . . . . . . . . . . . . . 5-24
CORBA. . . . . . . . . . . . . . . . . . . . . . 4-13 Utilisation du menu contextuel du
Distribution d’applications de base de concepteur de menus . . . . . . . . . . . . . 5-25
données. . . . . . . . . . . . . . . . . . . . . . 4-13 Commandes du menu contextuel . . . . . 5-25
Changement de menu à la conception . . 5-26
Chapitre 5 Utilisation des modèles de menu . . . . . . 5-27
Conception de l’interface utilisateur Enregistrement d’un menu comme
modèle. . . . . . . . . . . . . . . . . . . . . . 5-28
des applications 5-1 Conventions de nom pour les éléments
TApplication, TScreen et TForm. . . . . . . . .5-1 et les gestionnaires d’événements des
Utilisation de la fiche principale . . . . . . . 5-1 modèles de menu . . . . . . . . . . . . . . 5-29
Ajout de fiches supplémentaires . . . . . . . 5-2 Manipulation d’éléments de menu à
Liaison de fiches . . . . . . . . . . . . . . . . . 5-2 l’exécution. . . . . . . . . . . . . . . . . . . . 5-29
Manipulation de l’application . . . . . . . . . 5-3 Fusion de menus . . . . . . . . . . . . . . . . 5-30
Gestion de l’écran . . . . . . . . . . . . . . . . 5-3 Spécification du menu actif : propriété
Menu . . . . . . . . . . . . . . . . . . . . . . 5-30
Gestion de la disposition . . . . . . . . . . . . 5-3 Ordre des éléments de menu fusionnés :
Utilisation des messages . . . . . . . . . . . . .5-4 propriété GroupIndex . . . . . . . . . . . . 5-30
Informations supplémentaires sur les fiches . .5-5 Importation de fichiers ressource . . . . . . 5-31
Contrôle du stockage en mémoire des Conception de barres d’outils et de barres
fiches . . . . . . . . . . . . . . . . . . . . . . . 5-5 multiples . . . . . . . . . . . . . . . . . . . . . 5-31
Affichage d’une fiche créée Ajout d’une barre d’outils en utilisant un
automatiquement . . . . . . . . . . . . . . . 5-6 composant volet . . . . . . . . . . . . . . . . 5-32
Création dynamique de fiche . . . . . . . . . 5-6 Ajout d’un turbobouton à un volet . . . . 5-33
Création de fiches non modales comme Spécification du glyphe d’un
fenêtres. . . . . . . . . . . . . . . . . . . . . . 5-7 turbobouton . . . . . . . . . . . . . . . . . . 5-33
Utilisation d’une variable locale pour créer Définition de l’état initial d’un
une instance de fiche . . . . . . . . . . . . . 5-7 turbobouton . . . . . . . . . . . . . . . . . . 5-33
Transfert de paramètres supplémentaires Création d’un groupe de turboboutons . . 5-34
aux fiches . . . . . . . . . . . . . . . . . . . . 5-8 Utilisation de boutons bascule . . . . . . . 5-34
Récupération de données des fiches . . . . . 5-9 Ajout d’une barre d’outils en utilisant le
Récupération de données dans des fiches composant barre d’outils. . . . . . . . . . . 5-34
non modales . . . . . . . . . . . . . . . . . . 5-9 Ajout d’un bouton outil . . . . . . . . . . . 5-35
Récupération de données dans des fiches Affectation d’images à des boutons
modales . . . . . . . . . . . . . . . . . . . . 5-10 outils . . . . . . . . . . . . . . . . . . . . . . 5-35
Réutilisation des composants et des Définition de l’aspect et de l’état initial
groupes de composants . . . . . . . . . . . . 5-12 d’un bouton outil . . . . . . . . . . . . . . 5-36
Création et utilisation des modèles de Création de groupes de boutons outils . . 5-36
composants . . . . . . . . . . . . . . . . . . . 5-13 Utilisation de boutons outils bascule. . . . 5-36
Manipulation des cadres . . . . . . . . . . . . 5-14 Ajout d’un composant barre multiple. . . . 5-37
Création des cadres . . . . . . . . . . . . . . . 5-14 Initialisation de l’aspect de la barre
Ajout de cadres à la palette de multiple . . . . . . . . . . . . . . . . . . . . 5-37
composants . . . . . . . . . . . . . . . . . . 5-14 Réponse aux clics . . . . . . . . . . . . . . . . 5-38
Utilisation et modification des cadres . . . . 5-15 Affectation d’un menu à un bouton outil . . 5-38
Partage des cadres . . . . . . . . . . . . . . . . 5-16 Ajout de barres d’outils masquées. . . . . . 5-38
Affichage d’une barre d’outils . . . . . . . . 5-38

iii
Utilisation des listes d’actions . . . . . . . . . 5-39 Gestion de l’événement OnPopup . . . . . . 6-12
Objets action . . . . . . . . . . . . . . . . . . . 5-39 Ajout de graphiques à des contrôles . . . . . 6-13
Utilisation des actions . . . . . . . . . . . . . . 5-41 Choix du style dessiné par le propriétaire . 6-13
Centralisation du code . . . . . . . . . . . . 5-41 Ajout d’objets graphiques à une liste de
Liaison de propriétés . . . . . . . . . . . . . 5-41 chaînes. . . . . . . . . . . . . . . . . . . . . . 6-14
Exécution d’actions . . . . . . . . . . . . . . 5-42 Ajout d’images à une application . . . . . 6-14
Actualisation des actions . . . . . . . . . . . 5-44 Ajout d’images à une liste de chaînes . . . 6-14
Classes d’actions prédéfinies. . . . . . . . . . 5-44 Dessiner des éléments dessinés par le
Actions standard d’édition. . . . . . . . . . 5-44 propriétaire . . . . . . . . . . . . . . . . . . 6-15
Actions standard de fenêtre . . . . . . . . . 5-45 Dimensionnement des éléments dessinés
Actions d’aide standard . . . . . . . . . . . 5-45 par le propriétaire . . . . . . . . . . . . . . . 6-15
Actions des ensembles de données . . . . 5-45 Dessin de chaque élément dessiné par le
Conception de composants utilisant des propriétaire . . . . . . . . . . . . . . . . . . . 6-17
actions . . . . . . . . . . . . . . . . . . . . . . 5-46
Comment les actions trouvent leurs Chapitre 7
cibles . . . . . . . . . . . . . . . . . . . . . . 5-46 Utilisation des graphiques et du
Recensement d’actions . . . . . . . . . . . . 5-48
Ecriture d’éditeurs de listes d’actions . . . 5-48 multimédia 7-1
Programmes exemple . . . . . . . . . . . . . . 5-48 Présentation de la programmation relative
aux graphiques . . . . . . . . . . . . . . . . . 7-1
Chapitre 6 Rafraîchissement de l’écran . . . . . . . . . . . 7-2
Manipulation des contrôles 6-1 Types des objets graphiques . . . . . . . . . . 7-3
Implémentation du glisser-déplacer dans Propriétés et méthodes communes du
canevas . . . . . . . . . . . . . . . . . . . . . . 7-4
les contrôles . . . . . . . . . . . . . . . . . . . .6-1
Utilisation des propriétés de l’objet canevas . 7-5
Début de l’opération glisser-déplacer . . . . 6-1
Utilisation des crayons . . . . . . . . . . . . . 7-5
Acceptation des éléments à déplacer. . . . . 6-2 Utilisation des pinceaux . . . . . . . . . . . . 7-8
Déplacement des éléments . . . . . . . . . . . 6-3 Lecture et définition de pixels. . . . . . . . . 7-9
Fin de l’opération glisser-déplacer . . . . . . 6-3 Utilisation des méthodes du canevas pour
Personnalisation du glisser-déplacer avec dessiner des objets graphiques . . . . . . . 7-10
un objet déplacement . . . . . . . . . . . . . 6-4 Dessin de lignes et de polylignes. . . . . . 7-10
Changement du pointeur de la souris . . . . 6-4 Dessin de formes. . . . . . . . . . . . . . . . 7-11
Implémentation du glisser-empiler dans Gestion de plusieurs objets de dessin dans
les contrôles . . . . . . . . . . . . . . . . . . . .6-4 votre application. . . . . . . . . . . . . . . . 7-12
Transformation d’un contrôle fenêtré en un Faire le suivi de l’outil de dessin à
site d’empilement . . . . . . . . . . . . . . . 6-5 utiliser . . . . . . . . . . . . . . . . . . . . . 7-12
Transformation d'un contrôle en un enfant Changement d’outil en utilisant un
empilable. . . . . . . . . . . . . . . . . . . . . 6-5 turbobouton . . . . . . . . . . . . . . . . . 7-13
Contrôle de l'empilement des contrôles Utilisation des outils de dessin . . . . . . . 7-14
enfant . . . . . . . . . . . . . . . . . . . . . . . 6-5 Dessiner sur un graphique . . . . . . . . . . 7-17
Contrôle du désempilement des contrôles Création de graphiques défilables . . . . . 7-17
enfant . . . . . . . . . . . . . . . . . . . . . . . 6-6 Ajout d’un contrôle image . . . . . . . . . . 7-18
Contrôle de la réponse des contrôles enfant Chargement et enregistrement de fichiers
aux opérations glisser-empiler . . . . . . . . 6-7 graphiques . . . . . . . . . . . . . . . . . . . 7-20
Manipulation du texte dans les contrôles . . .6-7 Chargement d’une image depuis un
Définition de l’alignement du texte . . . . . 6-7 fichier. . . . . . . . . . . . . . . . . . . . . . 7-20
Ajout de barres de défilement en mode Enregistrement d’une image dans un
exécution . . . . . . . . . . . . . . . . . . . . . 6-8 fichier. . . . . . . . . . . . . . . . . . . . . . 7-20
Ajout de l’objet Clipboard . . . . . . . . . . . 6-9 Remplacement de l’image . . . . . . . . . . 7-21
Sélection de texte. . . . . . . . . . . . . . . . . 6-9 Utilisation du Presse-papiers avec les
graphiques . . . . . . . . . . . . . . . . . . . 7-22
Sélection de la totalité d’un texte . . . . . . . 6-10
Copier des graphiques dans le
Couper, copier et coller du texte . . . . . . . 6-10 Presse-papiers. . . . . . . . . . . . . . . . . 7-22
Effacement du texte sélectionné . . . . . . . . 6-11 Couper des graphiques dans le
Désactivation des éléments de menu . . . . 6-11 Presse-papiers. . . . . . . . . . . . . . . . . 7-23
Ajout d’un menu surgissant . . . . . . . . . . 6-12

iv
Coller des graphiques depuis le Chapitre 9
Presse-papiers . . . . . . . . . . . . . . . . 7-23
Techniques de dessin dans une Utilisation des paquets
application . . . . . . . . . . . . . . . . . . . . 7-24 et des composants 9-1
Répondre à la souris . . . . . . . . . . . . . . 7-24
Ajout d’un champ à un objet fiche . . . . 7-27
Pourquoi utiliser des paquets ? . . . . . . . . 9-2
Amélioration du dessin des lignes . . . . . 7-28 Les paquets et les DLL standard. . . . . . . . 9-2
Utilisation du multimédia . . . . . . . . . . . 7-30 Paquets d’exécution . . . . . . . . . . . . . . . 9-3
Ajout de séquences vidéo silencieuses à Utilisation des paquets dans une
une application . . . . . . . . . . . . . . . . . 7-30 application . . . . . . . . . . . . . . . . . . . . 9-3
Exemple d'ajout de séquences vidéo Paquets chargés dynamiquement . . . . . . . 9-4
silencieuses . . . . . . . . . . . . . . . . . . 7-31 Choix des paquets d’exécution à utiliser . . . 9-4
Ajout de séquences audio et/ou vidéo à Paquets personnalisés . . . . . . . . . . . . . . 9-5
une application . . . . . . . . . . . . . . . . . 7-32 Paquets de conception. . . . . . . . . . . . . . 9-6
Exemple d'ajout de séquences audio Installation de paquets de composants . . . . 9-6
et / ou vidéo . . . . . . . . . . . . . . . . . 7-34 Création et modification de paquets . . . . . 9-8
Création d’un paquet. . . . . . . . . . . . . . . 9-8
Chapitre 8 Modification d’un paquet existant . . . . . . . 9-9
Ecriture d’applications multithreads 8-1 Modification manuelle de fichiers source
Définition d’objets thread. . . . . . . . . . . . .8-2 de paquets . . . . . . . . . . . . . . . . . . . . 9-9
Initialisation du thread . . . . . . . . . . . . . 8-3 Présentation de la structure d’un paquet. . 9-10
Affectation d’une priorité par défaut . . . . 8-3 Nom de paquets . . . . . . . . . . . . . . . . 9-10
Libération des threads . . . . . . . . . . . . . 8-3 La clause Requires . . . . . . . . . . . . . . . 9-10
Ecriture de la fonction thread . . . . . . . . . 8-4 La clause Contains. . . . . . . . . . . . . . . 9-11
Utilisation du thread principal VCL . . . . . 8-4 Compilation de paquets . . . . . . . . . . . . 9-11
Utilisation de variables locales aux Directives de compilation propres aux
threads . . . . . . . . . . . . . . . . . . . . . . 8-5 paquets . . . . . . . . . . . . . . . . . . . . . 9-11
Vérification de l’arrêt par d’autres threads . . 8-6 Utilisation du compilateur et du lieur en
ligne de commande . . . . . . . . . . . . . 9-13
Ecriture du code de nettoyage . . . . . . . . 8-6
Fichiers paquets créés lors d’une
Coordination de threads . . . . . . . . . . . . .8-7 compilation réussie . . . . . . . . . . . . . 9-13
Eviter les accès simultanés . . . . . . . . . . . 8-7 Déploiement de paquets . . . . . . . . . . . . 9-14
Verrouillage d’objets. . . . . . . . . . . . . . . 8-7 Déploiement d’applications utilisant des
Utilisation de sections critiques . . . . . . . . 8-7 paquets . . . . . . . . . . . . . . . . . . . . . 9-14
Utilisation du synchronisateur à écriture Distribution de paquets à d’autres
exclusive et lecture multiple. . . . . . . . . 8-8 développeurs . . . . . . . . . . . . . . . . . . 9-14
Autres techniques de partage de la Fichiers de collection de paquet . . . . . . 9-14
mémoire . . . . . . . . . . . . . . . . . . . . . 8-9
Attente des autres threads . . . . . . . . . . . 8-9
Attente de la fin d’exécution d’un thread . 8-9
Chapitre 10
Attente de l’achèvement d’une tâche . . . 8-10 Création d’applications
Exécution d’objets thread . . . . . . . . . . . . 8-11 internationales 10-1
Redéfinition de la priorité par défaut . . . . 8-11
Internationalisation et localisation . . . . . . . 10-1
Démarrage et arrêt des threads . . . . . . . . 8-12
Internationalisation . . . . . . . . . . . . . . . 10-1
Utilisation des threads dans les applications Localisation . . . . . . . . . . . . . . . . . . . . 10-1
distribuées . . . . . . . . . . . . . . . . . . . . 8-12
Internationalisation des applications . . . . . 10-2
Utilisation des threads dans les serveurs
de messagerie . . . . . . . . . . . . . . . . . . 8-12 Codage de l’application . . . . . . . . . . . . 10-2
Utilisation des threads avec les objets Jeux de caractères . . . . . . . . . . . . . . . 10-2
distribués. . . . . . . . . . . . . . . . . . . . . 8-13 Jeux de caractères OEM et ANSI . . . . . . 10-2
Ecriture d'applications (fichiers .EXE) . . . 8-13 Jeux de caractères sur deux octets . . . . . 10-2
Ecriture de bibliothèques. . . . . . . . . . . 8-14 Caractères larges . . . . . . . . . . . . . . . . 10-3
Inclure des fonctionnalités
Débogage d’applications multithreads . . . . 8-15 bi-directionnelles dans les applications . 10-4
Propriété BiDiMode . . . . . . . . . . . . . . 10-6

v
Fonctionnalités spécifiques aux cibles Partie II
locales . . . . . . . . . . . . . . . . . . . . . 10-8
Conception de l’interface utilisateur . . . . . 10-8 Développement d’applications
Texte . . . . . . . . . . . . . . . . . . . . . . . 10-9
Images graphiques . . . . . . . . . . . . . . 10-9
de base de données
Formats et ordre de tri . . . . . . . . . . . . 10-9
Correspondances entre claviers . . . . . . .10-10 Chapitre 12
Isolement des ressources . . . . . . . . . . . 10-10 Conception d’applications de
Création de DLL de ressources . . . . . . . 10-10 bases de données 12-1
Utilisation des DLL de ressource . . . . . . 10-12
Basculement dynamique de DLL de Utilisation des bases de données . . . . . . . 12-2
ressource . . . . . . . . . . . . . . . . . . . . 10-13 Types de bases de données . . . . . . . . . . 12-2
Localisation des applications. . . . . . . . . 10-13 Bases de données locales . . . . . . . . . . . 12-3
Localisation des ressources . . . . . . . . . . 10-13 Serveurs de bases de données distants . . 12-3
Sécurité des bases de données . . . . . . . . 12-4
Chapitre 11 Transactions. . . . . . . . . . . . . . . . . . . . 12-5
Dictionnaire de données . . . . . . . . . . . . 12-5
Déploiement des applications 11-1 Intégrité référentielle, procédures stockées
Déploiement d’applications généralistes. . . 11-1 et déclencheurs. . . . . . . . . . . . . . . . . 12-7
Utilisation des programmes d’installation. . 11-2 Architecture des bases de données . . . . . . 12-7
Identification des fichiers de Anticipation de l’évolutivité. . . . . . . . . . 12-8
l’application . . . . . . . . . . . . . . . . . . 11-2 Applications de base de données à niveau
Les fichiers de l’application, par unique . . . . . . . . . . . . . . . . . . . . . 12-10
extension de fichier . . . . . . . . . . . . . 11-3 Applications de base de données à niveau
Fichiers paquet . . . . . . . . . . . . . . . . . 11-3 double . . . . . . . . . . . . . . . . . . . . . 12-10
Contrôles ActiveX . . . . . . . . . . . . . . . 11-3 Applications de base de données
Applications complémentaires . . . . . . . 11-4 multiniveaux . . . . . . . . . . . . . . . . . .12-11
Emplacement des DLL . . . . . . . . . . . . 11-4
Conception de l’interface utilisateur . . . . 12-13
Déploiement d’applications de base de Affichage d’un seul enregistrement . . . . 12-13
données. . . . . . . . . . . . . . . . . . . . . . 11-4 Affichage de plusieurs enregistrements. . 12-14
L’accès au moteur de bases de données. . . 11-4 Analyse des données . . . . . . . . . . . . . 12-15
Le moteur de bases de données Borland . 11-5
Sélection des données à afficher . . . . . . 12-15
Autres moteurs de bases de données . . . 11-5
Ecriture de rapports. . . . . . . . . . . . . . 12-18
SQL Links. . . . . . . . . . . . . . . . . . . . 11-5
MIDAS, services d’application distribuée Chapitre 13
multiniveau . . . . . . . . . . . . . . . . . . . 11-6
Déploiement d’applications Web . . . . . . . 11-7 Construction d’applications à
Programmer pour des environnements niveau unique et à niveau double 13-1
hôtes hétérogènes. . . . . . . . . . . . . . . . 11-8 Applications basées sur le BDE . . . . . . . . 13-2
Résolution d’écran et profondeurs de Architecture basée sur le BDE . . . . . . . . 13-2
couleur . . . . . . . . . . . . . . . . . . . . . . 11-8 Présentation des bases de données et des
Si vous n’utilisez pas de redimension- ensembles de données . . . . . . . . . . . 13-3
nement dynamique . . . . . . . . . . . . . 11-9 Utilisation des sessions . . . . . . . . . . . . 13-4
Si vous redimensionnez dynamiquement
les fiches et les contrôles . . . . . . . . . . 11-9 Connexion aux bases de données . . . . . . 13-5
Adaptation à des profondeurs de Utilisation des transactions . . . . . . . . . . 13-5
couleur variables. . . . . . . . . . . . . . .11-10 Contrôle explicite des transactions . . . . . 13-6
Fontes. . . . . . . . . . . . . . . . . . . . . . . 11-11 Utilisation d’un composant base de
données pour les transactions . . . . . . . 13-7
Versions de Windows . . . . . . . . . . . . . 11-11
Utilisation de la propriété TransIsolation . 13-8
Termes du contrat de licence logicielle . . 11-12 Utilisation du SQL direct. . . . . . . . . . . 13-9
DEPLOY.TXT. . . . . . . . . . . . . . . . . . 11-12 Utilisation des transactions locales . . . . 13-10
README.TXT . . . . . . . . . . . . . . . . . 11-12 Mise en mémoire cache des mises à jour .13-11
Contrat de licence . . . . . . . . . . . . . . . 11-12 Création et restructuration des tables de
Documentation de produits vendus par base de données . . . . . . . . . . . . . . . .13-11
un tiers . . . . . . . . . . . . . . . . . . . . . 11-12

vi
Applications basées sur ADO . . . . . . . . 13-12 Extension de l’interface du serveur
Architecture basée sur ADO . . . . . . . . . 13-12 d’applications. . . . . . . . . . . . . . . . . 14-19
Bases de données et ensembles de Ajout de rappels à l’interface du serveur
données ADO . . . . . . . . . . . . . . . .13-13 d’applications . . . . . . . . . . . . . . . . 14-20
Connexion aux bases de données ADO. . 13-13 Extension de l’interface du serveur
Accès aux données. . . . . . . . . . . . . . . 13-14 d’applications lors de l’utilisation de
MTS. . . . . . . . . . . . . . . . . . . . . . 14-20
Création et restructuration de tables de bases
de données ADO. . . . . . . . . . . . . . . 13-15 Création de l’application client . . . . . . . 14-21
Applications de base de données Connexion au serveur d’applications . . . 14-22
linéaires . . . . . . . . . . . . . . . . . . . . . 13-15 Spécification d’une connexion à l’aide
de DCOM . . . . . . . . . . . . . . . . . . 14-23
Création des ensembles de données . . . . 13-16
Spécification d’une connexion à l’aide
Création d’un nouvel ensemble de de sockets . . . . . . . . . . . . . . . . . . 14-23
données à l’aide de champs persistants.13-16
Spécification d’une connexion à l’aide
Création d’un ensemble de données à de HTTP . . . . . . . . . . . . . . . . . . . 14-24
l’aide de définitions de champ et
d’index. . . . . . . . . . . . . . . . . . . . .13-17 Spécification d’une connexion à l’aide
de OLEnterprise . . . . . . . . . . . . . . 14-25
Création d’un ensemble de données à
partir d’une table existante . . . . . . . .13-18 Spécification d’une connexion à l’aide
de CORBA. . . . . . . . . . . . . . . . . . 14-25
Chargement et enregistrement des Courtage de connexions . . . . . . . . . . 14-26
données. . . . . . . . . . . . . . . . . . . . . 13-19
Gestion des connexions serveur . . . . . . 14-26
Utilisation du modèle “briefcase”. . . . . . 13-20
Connexion au serveur. . . . . . . . . . . . 14-26
Passage à une application à niveau triple. 13-21 Fermeture ou changement de connexion
serveur . . . . . . . . . . . . . . . . . . . . 14-27
Chapitre 14 Appel des interfaces serveur . . . . . . . . 14-27
Création d’applications Gestion des transactions dans les
multiniveaux 14-1 applications multiniveaux . . . . . . . . . . 14-29
Gestion des relations maître / détail . . . . 14-30
Avantages du modèle de base de données
multiniveau . . . . . . . . . . . . . . . . . . . 14-2 Gestion des informations d’état dans les
modules de données distants . . . . . . . 14-30
Présentation de la technologie MIDAS . . . 14-3
Présentation d’une application multiniveau
Ecriture des applications MIDAS Web . . . 14-32
basée sur MIDAS. . . . . . . . . . . . . . . . 14-3 Distribution d’une application client en
Structure de l’application client . . . . . . . . 14-4 tant que contrôle ActiveX . . . . . . . . . 14-33
Création d’une fiche active pour
Structure du serveur d’applications . . . . . 14-5 l’application client . . . . . . . . . . . . . 14-34
Utilisation de MTS . . . . . . . . . . . . . . 14-6 Construction des applications Web avec
Regroupement des modules de données InternetExpress. . . . . . . . . . . . . . . . 14-34
distants . . . . . . . . . . . . . . . . . . . . 14-8
Construction d’une application
Utilisation de l’interface IAppServer. . . . 14-8 InternetExpress . . . . . . . . . . . . . . . 14-35
Sélection d’un protocole de connexion. . . . 14-9 Utilisation des bibliothèques javascript . 14-36
Utilisation de connexions DCOM . . . . .14-10 Droits d’accès au serveur d’applications
Utilisation de connexions Socket . . . . . .14-10 et à son lancement. . . . . . . . . . . . . 14-37
Utilisation de connexions Web . . . . . . .14-11 Utilisation d’un courtier XML . . . . . . . 14-38
Utilisation de OLEnterprise . . . . . . . . .14-12 Lecture des paquets de données XML . 14-38
Utilisation de connexions CORBA . . . . .14-12 Application des mises à jour à partir des
Construction d’une application paquets delta XML . . . . . . . . . . . . 14-39
multiniveau . . . . . . . . . . . . . . . . . . 14-12 Création des pages Web avec un
Création du serveur d’applications. . . . . 14-13 producteur de page MIDAS. . . . . . . . 14-40
Configuration du module de données Utilisation de l’éditeur de pages Web . . 14-41
distant . . . . . . . . . . . . . . . . . . . . . 14-15 Définition des propriétés des éléments
Configuration de TRemoteDataModule . .14-15 Web . . . . . . . . . . . . . . . . . . . . . . 14-42
Configuration de TMTSDataModule. . . .14-16 Personnalisation du modèle d’un
Configuration de TCorbaDataModule . . .14-17 producteur de page MIDAS . . . . . . . 14-43
Création d’un fournisseur de données
pour le serveur d’applications . . . . . . . 14-19

vii
Chapitre 15 Recherche de la connexion d’une base de
données . . . . . . . . . . . . . . . . . . . . . 16-9
Utilisation des composants Extraction d’informations sur une
fournisseur 15-1 session . . . . . . . . . . . . . . . . . . . . . 16-10
Manipulation des alias BDE. . . . . . . . . .16-11
Spécification de la source de données . . . . 15-1
Spécification de la visibilité des alias . . 16-11
Comment appliquer les modifications . . . . 15-2 Comment rendre des alias visibles aux
Contrôle des informations placées dans les autres sessions et applications. . . . . . 16-11
paquets de données . . . . . . . . . . . . . . 15-2 Comment déterminer les alias, les pilotes
Spécification des champs apparaissant et les paramètres connus . . . . . . . . . 16-11
dans les paquets de données . . . . . . . . 15-3 Création, modification et suppression
Initialisation des options contrôlant les des alias . . . . . . . . . . . . . . . . . . . 16-12
paquets de données . . . . . . . . . . . . . . 15-3 Déplacement parmi les composants base
Ajout d’informations personnalisées aux de données d’une session . . . . . . . . . 16-13
paquets de données . . . . . . . . . . . . . . 15-5 Spécification de l’emplacement des
Comment répondre aux demandes de répertoires Paradox . . . . . . . . . . . . . 16-14
données des clients. . . . . . . . . . . . . . . 15-5 Spécification de l’emplacement du
fichier de contrôle . . . . . . . . . . . . . 16-14
Comment répondre aux demandes de Spécification de l’emplacement des
mise à jour des clients. . . . . . . . . . . . . 15-6 fichiers temporaires . . . . . . . . . . . . 16-15
Modification des paquets delta avant la Manipulation de tables Paradox et dBase
mise à jour de la base de données . . . . . 15-7 protégées par mot de passe . . . . . . . . 16-15
Comment contrôler l’application des mises Utilisation de la méthode
à jour . . . . . . . . . . . . . . . . . . . . . . . 15-8 AddPassword . . . . . . . . . . . . . . . . 16-15
Filtrage des mises à jour . . . . . . . . . . . . 15-9 Utilisation des méthodes
Résolution des erreurs de mise à jour par RemovePassword et
le fournisseur . . . . . . . . . . . . . . . . . 15-10 RemoveAllPasswords . . . . . . . . . . . 16-16
Application des mises à jour à des Utilisation de la méthode GetPassword
ensembles de données représentant et de l’événement OnPassword . . . . . 16-16
plusieurs tables . . . . . . . . . . . . . . . . 15-10 Gestion de plusieurs sessions . . . . . . . . 16-17
Comment répondre aux événements Utilisation d’un composant session dans
générés par le client . . . . . . . . . . . . . 15-11 des modules de données . . . . . . . . . . 16-19
Gestion des contraintes du serveur. . . . . 15-11
Chapitre 17
Chapitre 16 Connexion aux bases de données 17-1
Gestion de sessions de bases Présentation des composants base de
de données 16-1 données persistants et temporaires . . . . . 17-1
Manipulation d’un composant session. . . . 16-2 Utilisation des composants base de
données temporaires . . . . . . . . . . . . . 17-2
Utilisation de la session par défaut. . . . . . 16-2
Création de composants base de données
Création de sessions supplémentaires . . . . 16-3 en mode conception . . . . . . . . . . . . . 17-3
Dénomination d’une session . . . . . . . . . . 16-4 Création de composants base de données
Activation d’une session . . . . . . . . . . . . 16-5 à l’exécution . . . . . . . . . . . . . . . . . . 17-3
Personnalisation du démarrage d’une Contrôle des connexions . . . . . . . . . . . . 17-4
session . . . . . . . . . . . . . . . . . . . . . . 16-6
Association d’un composant base de
Spécification du comportement par défaut données à une session . . . . . . . . . . . . 17-4
de connexion aux bases de données . . . . 16-6
Spécification d’un alias BDE . . . . . . . . . 17-5
Création, ouverture et fermeture des
connexions de bases de données . . . . . . 16-7 Définition des paramètres des alias BDE. . 17-6
Fermeture d’une connexion de base de Contrôle de la connexion au serveur . . . . 17-7
données . . . . . . . . . . . . . . . . . . . . 16-8 Connexion à un serveur de bases de
Fermeture de toutes les connexions de données . . . . . . . . . . . . . . . . . . . . . 17-7
base de données . . . . . . . . . . . . . . . 16-8 Considérations relatives à la connexion à
Abandon des connexions aux bases de un serveur distant. . . . . . . . . . . . . . . 17-8
données temporaires. . . . . . . . . . . . . . 16-9 Utilisation des protocoles réseau . . . . . . 17-8
Utilisation de ODBC. . . . . . . . . . . . . . 17-9

viii
Déconnexion d’un serveur de base de Utilisation de la méthode Lookup . . . . . 18-18
données. . . . . . . . . . . . . . . . . . . . . . 17-9 Affichage et édition d’ensembles de
Fermeture d’ensembles de données sans données en utilisant des filtres. . . . . . . 18-19
déconnexion du serveur . . . . . . . . . . . 17-9 Activation et désactivation des filtres . . . 18-19
Déplacement parmi les ensembles de Création de filtres . . . . . . . . . . . . . . . 18-19
données d’un composant base de Définition de la propriété Filter . . . . . . 18-20
données. . . . . . . . . . . . . . . . . . . . . 17-10
Ecriture d’un gestionnaire d’événement
Interactions entre les composants base de OnFilterRecord . . . . . . . . . . . . . . . . 18-21
données et les composants session . . . . 17-10 Permutation entre les gestionnaires
Utilisation de composants base de données d’événement filtre à l’exécution . . . . . 18-22
dans des modules de données. . . . . . . 17-11 Définition d’options de filtre . . . . . . . . 18-22
Exécution d’instructions SQL depuis un Navigation parmi les enregistrements
composant TDatabase . . . . . . . . . . . . 17-11 d’un ensemble de données filtré . . . . . 18-23
Exécution d’instructions SQL sans Modification des données. . . . . . . . . . . 18-24
ensemble de résultats . . . . . . . . . . . . 17-11 Modification d’enregistrements . . . . . . . 18-24
Exécution d’instructions SQL avec Ajout de nouveaux enregistrements . . . . 18-25
ensembles de résultats. . . . . . . . . . . . 17-12 Insertion d’enregistrements. . . . . . . . . 18-26
Exécution d’instructions SQL Ajout d’enregistrements. . . . . . . . . . . 18-26
paramétrées . . . . . . . . . . . . . . . . . . 17-13 Suppression d’enregistrements . . . . . . . 18-27
Validation des modifications . . . . . . . . 18-27
Chapitre 18 Annulation des modifications. . . . . . . . 18-27
Présentation des ensembles de Modification d’enregistrements entiers . . 18-28
données 18-1 Utilisation des événements des ensembles
de données . . . . . . . . . . . . . . . . . . . 18-29
Présentation de l’objet TDataSet . . . . . . . 18-2 Interruption d’une méthode. . . . . . . . . 18-30
Types d’ensembles de données . . . . . . . . 18-3 Utilisation de l’événement OnCalcFields . 18-30
Ouverture et fermeture des ensembles de Utilisation des ensembles de données
données. . . . . . . . . . . . . . . . . . . . . . 18-3 orientés BDE . . . . . . . . . . . . . . . . . . 18-31
Détermination et définition des états d’un Présentation de l’orientation BDE . . . . . 18-32
ensemble de données . . . . . . . . . . . . . 18-4 Gestion des connexions de base de
Désactivation d’un ensemble de données . . 18-6 données et de session. . . . . . . . . . . . 18-32
Visualisation d’un ensemble de données . . 18-7 Utilisation des propriétés DatabaseName
Activation de l’édition d’un ensemble de et SessionName. . . . . . . . . . . . . . . 18-33
données. . . . . . . . . . . . . . . . . . . . . . 18-8 Utilisation des propriétés de handle
Activation de l’insertion de nouveaux BDE . . . . . . . . . . . . . . . . . . . . . . 18-33
enregistrements . . . . . . . . . . . . . . . . . 18-8 Utilisation des mises à jour en mémoire
Activation de recherches indexées et cache . . . . . . . . . . . . . . . . . . . . . . 18-33
définition de portées. . . . . . . . . . . . . . 18-9 Mise en mémoire cache des BLOB . . . . 18-35
Champs calculés . . . . . . . . . . . . . . . . 18-10
Filtrage d’enregistrements . . . . . . . . . . 18-10 Chapitre 19
Mise à jour d’enregistrements . . . . . . . . 18-10 Manipulation des composants
Navigation dans les ensembles de champ 19-1
données. . . . . . . . . . . . . . . . . . . . . 18-10
Utilisation des méthodes First et Last . . . 18-11 Présentation des composants champ . . . . . 19-2
Utilisation des méthodes Next et Prior . . 18-12 Champs dynamiques . . . . . . . . . . . . . . 19-3
Utilisation de la méthode MoveBy . . . . . 18-12 Champs persistants . . . . . . . . . . . . . . . 19-4
Utilisation des propriétés Eof et Bof . . . . 18-13 Création de champs persistants . . . . . . . . 19-6
Eof . . . . . . . . . . . . . . . . . . . . . . . .18-13 Modification de l’ordre des champs
Bof . . . . . . . . . . . . . . . . . . . . . . . .18-14 persistants . . . . . . . . . . . . . . . . . . . . 19-7
Marquage d’enregistrements . . . . . . . . . 18-15 Définition de nouveaux champs
Recherche dans les ensembles de persistants . . . . . . . . . . . . . . . . . . . . 19-7
données. . . . . . . . . . . . . . . . . . . . . 18-17 Définition d’un champ de données . . . . . 19-9
Utilisation de la méthode Locate . . . . . . 18-17 Définition d’un champ calculé . . . . . . . 19-10

ix
Programmation d’un champ calculé . . . . 19-10 Utilisation de champs de référence . . . . 19-31
Définition d’un champ de référence . . . . 19-11 Affichage des champs de référence . . . 19-32
Définition d’un champ agrégat . . . . . . . 19-13 Accès aux données d’un champ de
Suppression de champs persistants. . . . . 19-14 référence . . . . . . . . . . . . . . . . . . . 19-32
Définition des événements et des
propriétés des champs persistants . . . . 19-14 Chapitre 20
Définition des propriétés d’affichage et Manipulation des tables 20-1
d’édition en mode conception . . . . . . . 19-15 Utilisation des composants table . . . . . . . 20-1
Définition des propriétés des composants
champ à l’exécution . . . . . . . . . . . . . 19-16 Configuration d’un composant table . . . . . 20-2
Création d’ensembles d’attributs pour les Spécification de l’emplacement d’une base
composants champ. . . . . . . . . . . . . . 19-17 de données . . . . . . . . . . . . . . . . . . . 20-2
Association des ensembles d’attributs aux Spécification d’un nom de table . . . . . . . 20-3
composants champ. . . . . . . . . . . . . . 19-17 Spécification du type des tables locales. . . 20-3
Suppression des associations d’ensembles Ouverture et fermeture d’une table . . . . . 20-4
d’attributs . . . . . . . . . . . . . . . . . . . 19-18 Contrôle des privilèges d’écriture / lecture
Contrôle ou dissimulation de la saisie d’une table . . . . . . . . . . . . . . . . . . . . 20-5
utilisateur . . . . . . . . . . . . . . . . . . . 19-18 Recherche d’enregistrements . . . . . . . . . . 20-5
Utilisation des formats par défaut pour Recherche d’enregistrements à partir des
les champs numériques, date et heure. . 19-19 champs indexés . . . . . . . . . . . . . . . . 20-6
Gestion des événements . . . . . . . . . . . 19-20 Exécution d’une recherche avec les
Manipulation des méthodes de champ méthodes Goto . . . . . . . . . . . . . . . . 20-7
lors de l’exécution . . . . . . . . . . . . . . 19-20 Exécution d’une recherche avec les
méthodes Find . . . . . . . . . . . . . . . . 20-8
Affichage, conversion et accès aux valeurs
des champs . . . . . . . . . . . . . . . . . . 19-21 Spécification de l’enregistrement en cours
après une recherche. . . . . . . . . . . . . . 20-8
Affichage de valeurs dans les contrôles
standard . . . . . . . . . . . . . . . . . . . . 19-21 Recherche sur des clés partielles . . . . . . . 20-8
Conversion des valeurs de champs . . . . 19-22 Recherche avec un index secondaire . . . . 20-9
Accès à des valeurs par la propriété Réitération ou extension d’une recherche . 20-9
d’ensemble de données par défaut . . . . 19-23 Tri d’enregistrements. . . . . . . . . . . . . . 20-10
Accès à des valeurs par la propriété Extraction d’une liste d’index disponibles
Fields d’un ensemble de données. . . . . 19-23 avec GetIndexNames . . . . . . . . . . . . 20-10
Accès à des valeurs par la méthode Spécification d’un index secondaire avec
FieldByName d’un ensemble de IndexName . . . . . . . . . . . . . . . . . . 20-10
données. . . . . . . . . . . . . . . . . . . . . 19-24 Spécification d’un fichier d’index
Vérification de la valeur en cours d’un dBASE . . . . . . . . . . . . . . . . . . . . 20-10
champ. . . . . . . . . . . . . . . . . . . . . . 19-25 Spécification d’un ordre de tri pour les
tables SQL . . . . . . . . . . . . . . . . . . .20-11
Définition de la valeur par défaut d’un Spécification de champs avec
champ. . . . . . . . . . . . . . . . . . . . . . 19-25 IndexFieldNames. . . . . . . . . . . . . . 20-11
Utilisation de contraintes . . . . . . . . . . . 19-25 Vérification de la liste de champs d’un
Création d’une contrainte personnalisée. . 19-25 index . . . . . . . . . . . . . . . . . . . . . . 20-12
Utilisation des contraintes de serveur . . . 19-26 Manipulation d’un sous-ensemble de
Utilisation des champs objet . . . . . . . . . 19-27 données . . . . . . . . . . . . . . . . . . . . . 20-12
Affichage des champs ADT et tableau . . 19-27 Présentation des différences entre les
Utilisation des champs ADT. . . . . . . . . 19-28 portées et les filtres . . . . . . . . . . . . . 20-12
Accès aux valeurs de champ ADT. . . . .19-28 Création et application d’une nouvelle
Utilisation des champs tableau . . . . . . . 19-29 portée . . . . . . . . . . . . . . . . . . . . . 20-13
Accès aux valeurs de champs tableau. . .19-30 Définition des valeurs de début de
Utilisation des champs ensemble de portée. . . . . . . . . . . . . . . . . . . . . 20-13
données. . . . . . . . . . . . . . . . . . . . . 19-30 Définition des valeurs de fin de portée . 20-14
Affichage des champs ensemble de Définition des valeurs de début et de
données . . . . . . . . . . . . . . . . . . . .19-31 fin de portée . . . . . . . . . . . . . . . . 20-15
Accès aux données d’un ensemble de Spécification d’une portée à partir de
données imbriqué . . . . . . . . . . . . . .19-31 clés partielles . . . . . . . . . . . . . . . . 20-16

x
Inclusion ou exclusion d’enregistrements Spécification d’une instruction SQL à
correspondant aux valeurs d’une l’exécution. . . . . . . . . . . . . . . . . . . . 21-7
portée . . . . . . . . . . . . . . . . . . . . .20-16 Définition directe de la propriété SQL. . . 21-8
Application d’une portée. . . . . . . . . . .20-16 Chargement de la propriété SQL depuis
Annulation d’une portée . . . . . . . . . . .20-17 un fichier . . . . . . . . . . . . . . . . . . . 21-8
Modification d’une portée . . . . . . . . . . 20-17 Chargement de la propriété SQL depuis
Modification du début de la portée . . . .20-17 un objet liste de chaînes . . . . . . . . . . 21-9
Modification de la fin de la portée . . . .20-18 Définition de paramètres . . . . . . . . . . . . 21-9
Suppression de tous les enregistrements Attribution de paramètres en phase de
d’une table . . . . . . . . . . . . . . . . . . . 20-18 conception. . . . . . . . . . . . . . . . . . . 21-10
Suppression d’une table. . . . . . . . . . . . 20-18 Affectation de paramètres en phase
Changement du nom d’une table. . . . . . 20-18 d’exécution . . . . . . . . . . . . . . . . . . .21-11
Utilisation d’une source de données pour
Création d’une table . . . . . . . . . . . . . . 20-19 lier les paramètres. . . . . . . . . . . . . . .21-11
Importation des données d’une autre Exécution d’une requête. . . . . . . . . . . . 21-13
table . . . . . . . . . . . . . . . . . . . . . . . 20-21 Exécution d’une requête pendant la
Utilisation de TBatchMove . . . . . . . . . . 20-22 conception. . . . . . . . . . . . . . . . . . . 21-13
Création d’un composant action Exécution d’une requête pendant
groupée. . . . . . . . . . . . . . . . . . . . . 20-22 l’exécution. . . . . . . . . . . . . . . . . . . 21-14
Spécification d’un mode d’action Exécution d’une requête renvoyant un
groupée. . . . . . . . . . . . . . . . . . . . . 20-23 ensemble de résultats . . . . . . . . . . . 21-14
Ajout . . . . . . . . . . . . . . . . . . . . . . .20-24 Exécution d’une requête sans ensemble
Mise à jour . . . . . . . . . . . . . . . . . . .20-24 de résultats . . . . . . . . . . . . . . . . . 21-15
Ajout et mise à jour. . . . . . . . . . . . . .20-24 Préparation d’une requête . . . . . . . . . . 21-15
Copie. . . . . . . . . . . . . . . . . . . . . . .20-24 Réinitialisation de la préparation d’une
Suppression . . . . . . . . . . . . . . . . . . .20-25 requête . . . . . . . . . . . . . . . . . . . . . 21-15
Etablissement d’une correspondance Création de requêtes hétérogènes . . . . . . 21-16
entre les types de données . . . . . . . . . 20-25
Exécution d’une action groupée. . . . . . . 20-26 Amélioration des performances d’une
Gestion des erreurs relatives aux actions requête . . . . . . . . . . . . . . . . . . . . . 21-17
groupées . . . . . . . . . . . . . . . . . . . . 20-26 Désactivation des curseurs
bidirectionnels . . . . . . . . . . . . . . . . 21-17
Synchronisation de tables liées à la même
table . . . . . . . . . . . . . . . . . . . . . . . 20-27 Manipulation des ensembles de résultats . 21-17
Création de fiches maître-détail . . . . . . . 20-27 Activation de l’édition d’un ensemble de
résultats . . . . . . . . . . . . . . . . . . . . 21-18
Construction d’une fiche maître-détail Utilisation de SQL local avec les
exemple . . . . . . . . . . . . . . . . . . . . 20-28 ensembles de résultats modifiables . . . 21-18
Utilisation des tables imbriquées . . . . . . 20-29 Restrictions relatives aux requêtes
Configuration d’un composant table modifiables . . . . . . . . . . . . . . . . . 21-18
imbriquée . . . . . . . . . . . . . . . . . . . 20-29 Utilisation de SQL sur serveur distant
avec les ensembles de résultats
Chapitre 21 modifiables . . . . . . . . . . . . . . . . . . 21-19
Manipulation des requêtes 21-1 Restrictions sur la mise à jour d’un
ensemble de résultats modifiable. . . . . 21-19
Pour une utilisation efficace des requêtes. . 21-1 Mise à jour d’un ensemble de résultats
Informations pour les développeurs en lecture seulement . . . . . . . . . . . . 21-19
d’applications de bureau . . . . . . . . . . . 21-2
Informations pour les développeurs Chapitre 22
d’applications sur serveur . . . . . . . . . . 21-3
Bases de données accessibles par un Manipulation des procédures
composant requête . . . . . . . . . . . . . . . 21-4 stockées 22-1
Utilisation d’un composant requête . . . . . 21-4 Quand utiliser les procédures stockées ?. . . 22-2
Spécification de l’instruction SQL à Utilisation de procédures stockées . . . . . . 22-3
exécuter. . . . . . . . . . . . . . . . . . . . . . 21-6 Création d’un composant procédure
Spécification de la propriété SQL en phase stockée . . . . . . . . . . . . . . . . . . . . . . 22-4
de conception . . . . . . . . . . . . . . . . . . 21-7

xi
Création d’une procédure stockée . . . . . . 22-5 Activation et désactivation d’une
Préparation et exécution d’une procédure connexion . . . . . . . . . . . . . . . . . . . 23-5
stockée . . . . . . . . . . . . . . . . . . . . . . 22-5 Comment déterminer ce que fait un
Utilisation de procédures stockées qui composant connexion ? . . . . . . . . . . . 23-5
renvoient des ensembles de résultats . . . 22-6 Optimisation d’une connexion . . . . . . . . 23-6
Extraction d’un ensemble de résultat Spécification des attributs d’une
avec un composant TQuery . . . . . . . . 22-6 connexion . . . . . . . . . . . . . . . . . . . 23-6
Extraction d’un ensemble de résultat Contrôle des dépassements de délais . . . 23-7
avec un composant TStoredProc . . . . . 22-7 Contrôle de la boîte de dialogue
Utilisation de procédures stockées qui d’identification . . . . . . . . . . . . . . . . 23-8
renvoient des données à l’aide de Enumération des tables et procédures
paramètres . . . . . . . . . . . . . . . . . . . . 22-8 stockées . . . . . . . . . . . . . . . . . . . . . 23-9
Extraction de valeurs individuelles avec Accès aux ensembles de données d’une
un composant TQuery . . . . . . . . . . . 22-8 connexion . . . . . . . . . . . . . . . . . . . 23-9
Extraction de valeurs individuelles avec Accès aux commandes d’une connexion 23-10
un composant TStoredProc . . . . . . . . 22-9 Enumération des tables disponibles . . . 23-10
Utilisation de procédures stockées pour Enumération des procédures stockées
manipuler les données . . . . . . . . . . . . 22-9 disponibles . . . . . . . . . . . . . . . . . 23-11
Exécution d’une procédure stockée Utilisation des transactions de connexion .23-11
d’action avec un composant TQuery . .22-10 Utilisation des méthodes de transaction. 23-12
Exécution d’une procédure stockée Utilisation des événements de
d’action avec un composant transaction . . . . . . . . . . . . . . . . . . 23-12
TStoredProc . . . . . . . . . . . . . . . . . .22-10 Utilisation des ensembles de données
Présentation des paramètres des ADO . . . . . . . . . . . . . . . . . . . . . . . 23-12
procédures stockées . . . . . . . . . . . . . 22-11 Caractéristiques communes à tous les
Utilisation des paramètres d’entrée. . . . . 22-12 composants ensemble de données
Utilisation des paramètres de sortie . . . . 22-13 ADO . . . . . . . . . . . . . . . . . . . . . . 23-13
Utilisation des paramètres Modification des données . . . . . . . . . 23-13
d’entrée / sortie . . . . . . . . . . . . . . . . 22-13 Déplacement dans un ensemble de
Utilisation du paramètre résultat . . . . . . 22-14 données . . . . . . . . . . . . . . . . . . . 23-13
Accès aux paramètres en mode Utilisation des contrôles orientés
conception . . . . . . . . . . . . . . . . . . . 22-14 données . . . . . . . . . . . . . . . . . . . 23-14
Définition des informations sur les Connexion à un stockage de données en
paramètres à la conception. . . . . . . . . 22-15 utilisant des composants ensemble de
Création de paramètres en mode données ADO. . . . . . . . . . . . . . . . 23-14
exécution . . . . . . . . . . . . . . . . . . . . 22-16 Utilisation des ensembles
d’enregistrements. . . . . . . . . . . . . . 23-15
Liaison de paramètres. . . . . . . . . . . . . 22-17
Utilisation des mises à jour groupées . . 23-16
Visualisation des informations sur les Lecture et enregistrement des données
paramètres à la conception . . . . . . . . . 22-18 dans des fichiers . . . . . . . . . . . . . . 23-18
Manipulation des procédures stockées Utilisation de paramètres dans des
surchargées Oracle . . . . . . . . . . . . . . 22-19 commandes . . . . . . . . . . . . . . . . . 23-19
Utilisation de TADODataSet . . . . . . . . 23-20
Chapitre 23 Utilisation d’une commande pour
obtenir un ensemble de données . . . . 23-21
Utilisation des composants ADO 23-1 Utilisation de TADOTable . . . . . . . . . . 23-21
Présentation des composants ADO. . . . . . 23-1 Spécification de la table à utiliser. . . . . 23-22
Connexion à des stockages de données Utilisation de TADOQuery . . . . . . . . . 23-22
ADO. . . . . . . . . . . . . . . . . . . . . . . . 23-3 Spécification des instructions SQL . . . . 23-23
Connexion à un stockage de données avec Exécution d’instructions SQL . . . . . . . 23-23
TADOConnection. . . . . . . . . . . . . . . . 23-3 Utilisation de TADOStoredProc . . . . . . 23-24
Utilisation de TADOConnection ou de la Spécification de la procédure stockée . . 23-25
propriété ConnectionString d’un Exécution d’une procédure stockée. . . . 23-26
ensemble de données . . . . . . . . . . . . 23-3 Utilisation des paramètres de procédures
Spécification de la connexion . . . . . . . . 23-4 stockées . . . . . . . . . . . . . . . . . . . 23-26
Accès à l’objet connexion . . . . . . . . . . 23-4

xii
Exécution de commandes . . . . . . . . . . 23-28 Transmission de paramètres au serveur
Spécification de la commande . . . . . . .23-29 d’applications. . . . . . . . . . . . . . . . . 24-18
Utilisation de la méthode Execute . . . . .23-30 Envoi de paramètres de requête ou de
Annulation de commandes . . . . . . . . .23-30 procédure stockée . . . . . . . . . . . . . 24-19
Utilisation des ensembles de résultats Limitation des enregistrements avec
des commandes . . . . . . . . . . . . . . .23-30 des paramètres . . . . . . . . . . . . . . . 24-19
Gestion des paramètres de commande . .23-31 Redéfinition de l’ensemble de données
sur le serveur d’applications . . . . . . . 24-20
Chapitre 24 Extraction des données depuis un serveur
d’applications. . . . . . . . . . . . . . . . . 24-20
Création et utilisation d’un Gestion des contraintes. . . . . . . . . . . . 24-22
ensemble de données client 24-1 Gestion des contraintes depuis le
serveur . . . . . . . . . . . . . . . . . . . . 24-22
Manipulation des données avec un
Ajout de contraintes personnalisées . . . 24-23
ensemble de données client . . . . . . . . . 24-2
Mise à jour des enregistrements . . . . . . 24-24
Navigation parmi les données des
Application des mises à jour . . . . . . . 24-24
ensembles de données client. . . . . . . . . 24-2
Régularisation des erreurs de mise à
Limitation des enregistrements affichés . . . 24-3 jour . . . . . . . . . . . . . . . . . . . . . . 24-26
Représentation des relations Rafraîchissement des enregistrements . . . 24-27
maître / détail . . . . . . . . . . . . . . . . . . 24-3
Communication avec des fournisseurs à
Définition de contraintes pour les valeurs l’aide d’événements personnalisés . . . . 24-28
des données . . . . . . . . . . . . . . . . . . 24-4
Comment déclarer des données en lecture Utilisation d’un ensemble de données
seulement . . . . . . . . . . . . . . . . . . . . 24-5 client avec des données de fichier
Edition des données . . . . . . . . . . . . . . . 24-5 linéaire. . . . . . . . . . . . . . . . . . . . . . 24-28
Annulation des modifications . . . . . . . . 24-6 Création d’un nouvel ensemble de
Enregistrement des modifications . . . . . 24-6 données . . . . . . . . . . . . . . . . . . . . 24-29
Tri et indexation . . . . . . . . . . . . . . . . . 24-7 Chargement des données depuis un
fichier ou un flux . . . . . . . . . . . . . . 24-29
Ajout d’un nouvel index . . . . . . . . . . . 24-7
Suppression et permutation d’index . . . . 24-8 Fusion des modifications dans les
données . . . . . . . . . . . . . . . . . . . . 24-30
Utilisation des index pour regrouper les
données . . . . . . . . . . . . . . . . . . . . 24-9 Sauvegarde des données dans un fichier
Indexation à la volée . . . . . . . . . . . . .24-10 ou un flux. . . . . . . . . . . . . . . . . . . 24-30
Représentation des valeurs calculées. . . . 24-10
Utilisation de champs calculés de façon
Chapitre 25
interne dans les ensembles de données Manipulation des mises à jour
client . . . . . . . . . . . . . . . . . . . . . .24-10
Utilisation des agrégats maintenus . . . . . 24-11
en mémoire cache 25-1
Spécification d’agrégats. . . . . . . . . . . .24-11 Quand utiliser les mises à jour en mémoire
Agrégats de groupes d’enregistrements. .24-13 cache ?. . . . . . . . . . . . . . . . . . . . . . . 25-1
Obtention de valeurs d’agrégat . . . . . . .24-13 Utilisation des mises à jour en mémoire
Ajout d’informations d’application aux cache. . . . . . . . . . . . . . . . . . . . . . . . 25-2
données. . . . . . . . . . . . . . . . . . . . . 24-14 Activation et désactivation des mises à
Copie de données d’un autre ensemble de jour en mémoire cache . . . . . . . . . . . . 25-3
données. . . . . . . . . . . . . . . . . . . . . 24-14 Extraction d’enregistrements . . . . . . . . . 25-4
Affectation directe des données . . . . . . . 24-15 Application des mises à jour en mémoire
Clonage d’un curseur d’ensemble de cache . . . . . . . . . . . . . . . . . . . . . . . 25-5
données client. . . . . . . . . . . . . . . . . 24-16 Utilisation de la méthode d’un composant
Utilisation d’un ensemble de données base de données . . . . . . . . . . . . . . . 25-6
client avec un fournisseur de données. . 24-16 Utilisation des méthodes d’un composant
ensemble de données . . . . . . . . . . . . 25-7
Spécification d’un fournisseur de
données. . . . . . . . . . . . . . . . . . . . . 24-17 Application des mises à jour à des tables
maître / détail. . . . . . . . . . . . . . . . . 25-8
Obtention de paramètres du serveur Annulation des mises à jour en mémoire
d’applications . . . . . . . . . . . . . . . . . 24-17 cache en suspens . . . . . . . . . . . . . . . 25-9

xiii
Annulation des mises à jour en suspens Manipulation du texte d’un message
et désactivation des mises à jour d’erreur . . . . . . . . . . . . . . . . . . . . 25-30
suivantes. . . . . . . . . . . . . . . . . . . . 25-9 Accès aux propriétés OldValue,
Annulation des mises à jour en mémoire NewValue et CurValue d’un champ. . . 25-31
cache en suspens. . . . . . . . . . . . . . . 25-9
Annulation des mises à jour apportées à Chapitre 26
l’enregistrement en cours . . . . . . . . .25-10
Récupération d’enregistrements en Utilisation de contrôles de
mémoire cache . . . . . . . . . . . . . . . . 25-10 données 26-1
Spécification des enregistrements visibles
en mémoire cache . . . . . . . . . . . . . . 25-11 Fonctionnalités communes des contrôles
Vérification de l’état de la mise à jour. . . 25-12 de données . . . . . . . . . . . . . . . . . . . . 26-1
Association d’un contrôle de données à
Utilisation d’objets mise à jour pour un ensemble de données. . . . . . . . . . . 26-3
mettre à jour un ensemble de données . 25-13 Edition et mise à jour des données . . . . . 26-3
Spécification de la propriété UpdateObject Activation de l’édition des contrôles lors
d’un ensemble de données . . . . . . . . . 25-13 d’une saisie utilisateur . . . . . . . . . . . 26-3
Utilisation d’un seul objet mise à jour . .25-14 Edition des données affichées dans un
Utilisation de plusieurs objets mise à contrôle. . . . . . . . . . . . . . . . . . . . . 26-3
jour . . . . . . . . . . . . . . . . . . . . . . .25-14 Activation et désactivation de l’affichage
Création d’instructions SQL pour les des données . . . . . . . . . . . . . . . . . . 26-5
composants mise à jour. . . . . . . . . . . 25-15 Rafraîchissement de l’affichage des
Création d’instructions SQL lors de la données . . . . . . . . . . . . . . . . . . . . . 26-5
conception. . . . . . . . . . . . . . . . . . .25-16 Activation des événements souris, clavier
Substitution des paramètres dans les et timer . . . . . . . . . . . . . . . . . . . . . 26-6
instructions SQL de mise à jour . . . . .25-17
Elaboration des instructions SQL de Utilisation des sources de données . . . . . . 26-6
mise à jour . . . . . . . . . . . . . . . . . .25-18 Utilisation des propriétés de TDataSource . 26-6
Utilisation de la propriété Query d’un Propriété DataSet. . . . . . . . . . . . . . . . 26-7
composant mise à jour . . . . . . . . . . .25-19 Propriété Name. . . . . . . . . . . . . . . . . 26-7
Utilisation des propriétés DeleteSQL, Propriété Enabled . . . . . . . . . . . . . . . 26-7
InsertSQL et ModifySQL . . . . . . . . . .25-20 Propriété AutoEdit. . . . . . . . . . . . . . . 26-8
Exécution des instructions de mise à Utilisation des événements de
jour . . . . . . . . . . . . . . . . . . . . . . . 25-21 TDataSource . . . . . . . . . . . . . . . . . . 26-8
Appel de la méthode Apply . . . . . . . .25-21 Evénement OnDataChange . . . . . . . . . 26-8
Appel de la méthode SetParams . . . . . .25-22 Evénement OnUpdateData. . . . . . . . . . 26-8
Appel de la méthode ExecSQL . . . . . . .25-23 Evénement OnStateChange . . . . . . . . . 26-8
Utilisation de composants ensemble de Contrôles représentant un champ unique . . 26-9
données pour mettre à jour un Affichage de données en tant que libellés . 26-9
ensemble de données . . . . . . . . . . . . 25-24 Affichage et édition de champs dans une
Mise à jour d’un ensemble de résultat en boîte de saisie . . . . . . . . . . . . . . . . 26-10
lecture seule . . . . . . . . . . . . . . . . . . 25-25 Affichage et édition de texte dans un
Contrôle du processus de mise à jour . . . 25-25 contrôle mémo . . . . . . . . . . . . . . . . 26-10
Détermination de la nécessité de Affichage et édition dans un contrôle
contrôler le processus de mise à jour . . 25-26 mémo de texte formaté . . . . . . . . . . .26-11
Création d’un gestionnaire d’événement Affichage et édition de champs
OnUpdateRecord . . . . . . . . . . . . . . . 25-26 graphiques dans un contrôle image . . . 26-12
Gestion des erreurs de mise à jour en Affichage de données dans des boîtes
mémoire cache . . . . . . . . . . . . . . . . 25-28 liste et des boîtes à options . . . . . . . . 26-12
Référencement de l’ensemble de données Affichage et édition de données dans
à mettre à jour . . . . . . . . . . . . . . . . 25-28 une boîte liste. . . . . . . . . . . . . . . . 26-13
Indication du type de mise à jour ayant Affichage et édition de données dans
généré l’erreur . . . . . . . . . . . . . . . . 25-28 une boîte à options . . . . . . . . . . . . 26-13
Spécification de l’action à entreprendre . . 25-29 Affichage dans une boîte liste de
référence et une boîte à options de
référence. . . . . . . . . . . . . . . . . . . . 26-14

xiv
Spécification d’une liste basée sur un Chapitre 27
champ de référence . . . . . . . . . . . . .26-14
Propriétés des boîtes liste et des boîtes à Utilisation de composants d’aide
options de référence. . . . . . . . . . . . .26-15 à la décision 27-1
Recherche incrémentale dans les valeurs
d’une liste d’éléments. . . . . . . . . . . .26-16 Présentation . . . . . . . . . . . . . . . . . . . . 27-1
Manipulation de champs booléens avec Présentation des références croisées . . . . . 27-2
des cases à cocher . . . . . . . . . . . . . . 26-16 Références croisées à une dimension . . . . 27-3
Limitation de valeurs de champ avec des Références croisées à plusieurs dimensions 27-3
boutons radio . . . . . . . . . . . . . . . . . 26-17 Instructions relatives à l’utilisation de
Visualisation et édition des données avec composants d’aide à la décision . . . . . . . 27-3
un contrôle TDBGrid . . . . . . . . . . . . 26-18 Utilisation d’ensembles de données avec
Utilisation d’un contrôle grille à son état les composants d’aide à la décision. . . . . 27-5
par défaut . . . . . . . . . . . . . . . . . . . 26-19 Création d’ensembles de données de
Création d’une grille personnalisée. . . . . 26-20 décision avec TQuery ou TTable . . . . . . 27-6
Présentation des colonnes persistantes . .26-20 Création d’ensembles de données de
Détermination de la source d’une décision avec l’éditeur de requête de
propriété de colonne à l’exécution . . . .26-21 décision . . . . . . . . . . . . . . . . . . . . . 27-6
Création de colonnes persistantes . . . . .26-22 Utilisation de l’éditeur de requête de
Suppression de colonnes persistantes . . .26-23 décision . . . . . . . . . . . . . . . . . . . . 27-7
Modification de l’ordre des colonnes Propriétés d’une requête de décision . . . . 27-7
persistantes . . . . . . . . . . . . . . . . . .26-23
Utilisation des cubes de décision . . . . . . . 27-8
Définition d’une colonne de liste de
référence . . . . . . . . . . . . . . . . . . . .26-23 Propriétés et événements des cubes de
Définition d’une colonne de liste de décision . . . . . . . . . . . . . . . . . . . . . 27-8
choix . . . . . . . . . . . . . . . . . . . . . .26-23 Utilisation de l’éditeur de cube de
Insertion d’un bouton dans une décision . . . . . . . . . . . . . . . . . . . . . 27-8
colonne. . . . . . . . . . . . . . . . . . . . .26-24 Visualisation et modification des
Définition des propriétés de colonne en paramètres de dimensions . . . . . . . . . 27-9
mode conception. . . . . . . . . . . . . . .26-24 Définition du maximum de dimensions
Restauration des valeurs par défaut et de récapitulations . . . . . . . . . . . . . 27-9
d’une colonne . . . . . . . . . . . . . . . .26-25 Visualisation et modification des options
Affichage des champs ADT et tableau . . 26-25 de conception . . . . . . . . . . . . . . . . 27-10
Définition des options de la grille . . . . . 26-27 Utilisation de sources de décision. . . . . . 27-10
Saisie de modifications dans la grille . . . 26-28 Propriétés et événements . . . . . . . . . . 27-10
Changement de l’ordre des colonnes à la Utilisation de pivots de décision . . . . . . 27-11
conception . . . . . . . . . . . . . . . . . . . 26-29 Propriétés des pivots de décision . . . . . .27-11
Changement de l’ordre des colonnes à Création et utilisation de grilles de
l’exécution . . . . . . . . . . . . . . . . . . . 26-29 décision . . . . . . . . . . . . . . . . . . . . . 27-12
Contrôle du dessin de la grille . . . . . . .26-29 Création de grilles de décision . . . . . . . 27-12
Comment répondre aux actions de Utilisation de grilles de décision . . . . . . 27-12
l’utilisateur à l’exécution . . . . . . . . . . 26-30 Ouverture et fermeture des champs
Création d’une grille contenant d’autres d’une grille de décision. . . . . . . . . . 27-12
contrôles orientés données . . . . . . . . . 26-31 Réorganisation des lignes et des colonnes
Navigation et manipulation d’enregis- d’une grille de décision. . . . . . . . . . 27-13
trements . . . . . . . . . . . . . . . . . . . . 26-32 Perforation pour voir les détails dans les
Choix des boutons visibles. . . . . . . . . . 26-33 grilles de décision . . . . . . . . . . . . . 27-13
Affichage et dissimulation des boutons Limite des dimensions à sélectionner
en mode conception. . . . . . . . . . . . .26-34 dans les grilles de décision. . . . . . . . 27-13
Affichage et dissimulation des boutons Propriétés des grilles de décision . . . . . 27-14
à l’exécution . . . . . . . . . . . . . . . . .26-34 Création et utilisation de graphes de
Affichage de panneaux d’information . . . 26-35 décision . . . . . . . . . . . . . . . . . . . . . 27-15
Utilisation d’un navigateur pour Création de graphes de décision . . . . . . 27-15
plusieurs ensembles de données . . . . . 26-35 Utilisation de graphes de décision. . . . . 27-15

xv
Affichage du graphe de décision . . . . . . 27-17 Déploiement d’applications CORBA . . . . 28-18
Personnalisation du graphe de décision. . 27-17 Configuration de Smart Agents . . . . . . 28-19
Définition des modèles de graphe de Démarrage du Smart Agent . . . . . . . . 28-19
décision par défaut . . . . . . . . . . . . .27-18 Configuration de domaines ORB . . . . . 28-20
Personnalisation des séries d’un graphe Connexion de Smart Agents avec
de décision . . . . . . . . . . . . . . . . . .27-19 d’autres réseaux locaux . . . . . . . . . . 28-21
Utilisation des composants d’aide à la
décision à l’exécution . . . . . . . . . . . . 27-20 Chapitre 29
Pivots de décision à l’exécution . . . . . . . 27-20 Création d’applications serveur
Grilles de décision à l’exécution . . . . . . 27-21
Graphes de décision à l’exécution . . . . . 27-21 pour Internet 29-1
Considérations relatives au contrôle de la Terminologie et standard . . . . . . . . . . . . 29-1
mémoire . . . . . . . . . . . . . . . . . . . . 27-21 Composition d’un URL
Définition du maximum de dimensions, (Uniform Resource Locator) . . . . . . . . . 29-2
de champs récapitulatifs, et de cellules . 27-22 URI et URL . . . . . . . . . . . . . . . . . . . 29-2
Définition de l’état des dimensions. . . . . 27-22 En-tête de message de requête HTTP. . . . 29-3
Utilisation de dimensions paginées. . . . . 27-23 Activité d’un serveur HTTP . . . . . . . . . . 29-3
Composition des requêtes client . . . . . . . 29-3
Partie III Traitement des requêtes client par le
Ecriture d’applications distribuées serveur. . . . . . . . . . . . . . . . . . . . . . 29-4
Réponses aux requêtes client . . . . . . . . . 29-4
Applications serveur Web. . . . . . . . . . . . 29-5
Chapitre 28 Types d’applications serveur Web . . . . . . 29-5
Ecriture d’applications CORBA 28-1 ISAPI et NSAPI . . . . . . . . . . . . . . . . 29-5
Vue générale d’une application CORBA. . 28-2 CGI autonome . . . . . . . . . . . . . . . . . 29-6
Stubs et squelettes . . . . . . . . . . . . . . . . 28-3 Win-CGI autonome . . . . . . . . . . . . . . 29-6
Utilisation de Smart Agents . . . . . . . . . . 28-3 Création d’applications serveur Web . . . . 29-6
Activation d’applications serveur . . . . . . . 28-4 Le module Web . . . . . . . . . . . . . . . . . 29-6
Liaison dynamique d’appels d’interfaces . . 28-4 L’objet application Web . . . . . . . . . . . . 29-7
Ecriture de serveurs CORBA . . . . . . . . . 28-5 Structure d’une application serveur Web . . 29-8
Utilisation des experts CORBA . . . . . . . . 28-5 Le répartiteur Web . . . . . . . . . . . . . . . . 29-9
Définition d’interfaces d’objets . . . . . . . . 28-6 Ajout d’actions au répartiteur . . . . . . . . 29-9
Code généré automatiquement . . . . . . . . 28-8 Répartition des messages de requête . . . . 29-9
Recensement d’interfaces serveur . . . . . . . 28-9 Eléments d’action . . . . . . . . . . . . . . . . 29-10
Recensement d’interfaces avec le Choix du déclenchement des éléments
référentiel d’interfaces. . . . . . . . . . . . 28-9 d’action . . . . . . . . . . . . . . . . . . . . .29-11
Recensement d’interfaces avec l’Object URL de destination . . . . . . . . . . . . . 29-11
Activation Daemon . . . . . . . . . . . . .28-10 Type de méthode de requête . . . . . . . 29-11
Ecriture de clients CORBA . . . . . . . . . . 28-13 Activation et désactivation des éléments
Utilisation des stubs . . . . . . . . . . . . . . 28-13 d’action. . . . . . . . . . . . . . . . . . . . 29-11
Utilisation de l’interface d’appel Choix d’un élément d’action par défaut 29-12
dynamique. . . . . . . . . . . . . . . . . . . 28-14 Réponse aux messages de requête avec
Obtention de l’interface. . . . . . . . . . . .28-14 des éléments d’action. . . . . . . . . . . . 29-12
Appel d’interfaces avec DII . . . . . . . . .28-15 Envoi de la réponse . . . . . . . . . . . . . 29-13
Personnalisation d’applications CORBA. . 28-16 Utilisation de plusieurs éléments
d’action. . . . . . . . . . . . . . . . . . . . 29-13
Affichage d’objets dans l’interface
utilisateur . . . . . . . . . . . . . . . . . . . 28-17 Accès aux informations de requêtes
Présentation et dissimulation d’objets client . . . . . . . . . . . . . . . . . . . . . . . 29-14
CORBA. . . . . . . . . . . . . . . . . . . . . 28-17 Propriétés contenant des informations
Transmission d’informations client à des d’en-tête de requête . . . . . . . . . . . . . 29-14
objets serveur . . . . . . . . . . . . . . . . . 28-17 Propriétés identifiant la destination . . . 29-14
Propriétés décrivant le client Web . . . . 29-14

xvi
Propriétés identifiant le but de la Chapitre 30
requête . . . . . . . . . . . . . . . . . . . . .29-15
Propriétés décrivant la réponse attendue .29-15 Utilisation des sockets 30-1
Propriétés décrivant le contenu. . . . . . .29-15 Implémentation des services . . . . . . . . . . 30-1
Contenu d’un message de requête HTTP. 29-16 Description des protocoles de services . . . 30-2
Création de messages de réponse HTTP . 29-16 Communication avec les applications . . . 30-2
Informations d’en-tête de réponse . . . . . 29-16 Services et ports . . . . . . . . . . . . . . . . . 30-2
Indication du statut de la réponse . . . . .29-16 Types de connexions par socket . . . . . . . . 30-2
Indication d’attente d’une action du Connexions client . . . . . . . . . . . . . . . . 30-3
client . . . . . . . . . . . . . . . . . . . . . .29-17 Connexions d’écoute . . . . . . . . . . . . . . 30-3
Description de l’application serveur . . . .29-17 Connexions serveur . . . . . . . . . . . . . . . 30-3
Description du contenu . . . . . . . . . . .29-17
Description des sockets . . . . . . . . . . . . . 30-3
Définition du contenu de la réponse. . . . 29-17
Description des hôtes. . . . . . . . . . . . . . 30-4
Envoi de la réponse . . . . . . . . . . . . . . 29-18
Choix entre le nom de l’hôte et son
Génération du contenu des messages de adresse IP . . . . . . . . . . . . . . . . . . . 30-4
réponse . . . . . . . . . . . . . . . . . . . . . 29-18 Utilisation des ports . . . . . . . . . . . . . . 30-5
Utilisation du composant générateur de Utilisation des composants socket. . . . . . . 30-5
page. . . . . . . . . . . . . . . . . . . . . . . 29-19
Utilisation de sockets client . . . . . . . . . . 30-6
Modèles HTML . . . . . . . . . . . . . . . .29-19
Désignation du serveur souhaité . . . . . . 30-6
Choix du modèle HTML. . . . . . . . . . .29-20
Formation de la connexion. . . . . . . . . . 30-6
Conversion des balises HTML
transparentes . . . . . . . . . . . . . . . . .29-20 Obtention d’informations sur la
connexion . . . . . . . . . . . . . . . . . . . 30-6
Utilisation du générateur de page
depuis un élément d’action . . . . . . . .29-21 Fermeture de la connexion. . . . . . . . . . 30-7
Chaînage de générateurs de page . . . . .29-21 Utilisation de sockets serveur. . . . . . . . . 30-7
Désignation du port . . . . . . . . . . . . . . 30-7
Utilisation des bases de données dans les
Ecoute des requêtes client . . . . . . . . . . 30-7
réponses . . . . . . . . . . . . . . . . . . . . 29-23
Connexion aux clients. . . . . . . . . . . . . 30-7
Ajout d’une session au module Web. . . . 29-23 Obtenir des informations sur les
Représentation HTML d’une base de connexions. . . . . . . . . . . . . . . . . . . 30-8
données. . . . . . . . . . . . . . . . . . . . . 29-24 Fermeture des connexions serveur . . . . . 30-8
Utilisation des générateurs de page
ensemble de données . . . . . . . . . . . .29-24 Réponse aux événements socket . . . . . . . 30-8
Utilisation des générateurs de tableau . .29-24 Evénements d’erreurs. . . . . . . . . . . . . . 30-9
Choix des attributs de tableau . . . . . . .29-25 Evénements client . . . . . . . . . . . . . . . . 30-9
Choix des attributs de lignes . . . . . . . .29-25 Evénements serveur. . . . . . . . . . . . . . 30-10
Choix des attributs de colonnes . . . . . .29-25 Evénements d’écoute . . . . . . . . . . . . 30-10
Incorporation de tableaux dans un Evénements de connexions client. . . . . 30-10
document HTML . . . . . . . . . . . . . .29-26 Lectures et écritures sur des connexions
Configuration d’un générateur de socket . . . . . . . . . . . . . . . . . . . . . . 30-11
tableau ensemble de données . . . . . . .29-26 Connexions non bloquantes. . . . . . . . . .30-11
Configuration d’un générateur de Lecture et écriture d’événements . . . . . 30-11
tableau requête . . . . . . . . . . . . . . . .29-26 Connexions bloquantes. . . . . . . . . . . . 30-12
Débogage d’applications serveur . . . . . . 29-27 Utilisation de threads avec des
Débogage d’applications ISAPI et connexions bloquantes . . . . . . . . . . 30-12
NSAPI . . . . . . . . . . . . . . . . . . . . . 29-27 Utilisation de TWinSocketStream . . . . . 30-13
Débogage sous Windows NT . . . . . . . .29-27 Ecriture de threads client. . . . . . . . . . 30-13
Débogage avec Microsoft IIS Server . . . .29-27 Ecriture de threads serveur . . . . . . . . 30-14
Débogage sous MTS . . . . . . . . . . . . .29-28
Débogage avec Personal Web Server
pour Windows 95 . . . . . . . . . . . . . .29-29
Débogage avec Netscape Server
Version 2.0 . . . . . . . . . . . . . . . . . .29-30
Débogage d’applications CGI et
Win-CGI . . . . . . . . . . . . . . . . . . . . 29-31
Simulation du serveur . . . . . . . . . . . .29-31
Débogage en tant que DLL . . . . . . . . .29-31

xvii
Partie IV Définition de l’interface de conception . . . 32-7
Répartition des méthodes . . . . . . . . . . . . 32-8
Création de composants Méthodes statiques . . . . . . . . . . . . . . . 32-8
personnalisés Méthodes virtuelles . . . . . . . . . . . . . . . 32-9
Surcharge des méthodes . . . . . . . . . . . 32-9
Chapitre 31 Membres abstraits d’une classe . . . . . . . 32-10
Présentation générale de la Classes et pointeurs . . . . . . . . . . . . . . 32-10
création d’un composant 31-1 Chapitre 33
La bibliothèque des composants visuels. . . 31-1 Création de propriétés 33-1
Composants et classes. . . . . . . . . . . . . . 31-2 Pourquoi créer des propriétés ? . . . . . . . . 33-1
Comment créer un composant ? . . . . . . . 31-2 Types de propriétés . . . . . . . . . . . . . . . 33-2
Modification de contrôles existants. . . . . . 31-3 Publication des propriétés héritées . . . . . . 33-3
Création de contrôles fenêtrés . . . . . . . . . 31-4
Création de contrôles graphiques. . . . . . . 31-4 Définition des propriétés . . . . . . . . . . . . 33-4
Sous-classement de contrôles Windows . . . 31-4 Déclaration des propriétés. . . . . . . . . . . 33-4
Création de composants non visuels. . . . . 31-5 Stockage interne des données. . . . . . . . . 33-4
Accès direct. . . . . . . . . . . . . . . . . . . . 33-5
Contenu d’un composant ?. . . . . . . . . . . 31-5
Méthodes d’accès . . . . . . . . . . . . . . . . 33-5
Suppression des dépendances . . . . . . . . . 31-5 Méthode read . . . . . . . . . . . . . . . . . . 33-7
Propriétés, méthodes et événements . . . . . 31-6 Méthode write . . . . . . . . . . . . . . . . . 33-7
Propriétés . . . . . . . . . . . . . . . . . . . . 31-6 Valeurs par défaut d’une propriété . . . . . 33-8
Evénements . . . . . . . . . . . . . . . . . . . 31-7 Spécification d’aucune valeur par défaut . 33-8
Méthodes . . . . . . . . . . . . . . . . . . . . 31-7
Encapsulation des graphiques . . . . . . . . . 31-7 Création de propriétés tableau. . . . . . . . . 33-9
Recensement. . . . . . . . . . . . . . . . . . . . 31-8 Stockage et chargement des propriétés . . 33-10
Création d’un nouveau composant. . . . . . 31-8 Utilisation du mécanisme de stockage et
de chargement . . . . . . . . . . . . . . . . 33-10
Utilisation de l’expert composant. . . . . . . 31-9
Spécification des valeurs par défaut. . . . .33-11
Création manuelle d’un composant . . . . 31-11
Détermination du stockage . . . . . . . . . .33-11
Création d’un fichier unité. . . . . . . . . .31-11
Dérivation du composant . . . . . . . . . .31-12
Initialisation après chargement . . . . . . . 33-12
Recensement du composant . . . . . . . . .31-12 Stockage et chargement des propriétés
non publiées . . . . . . . . . . . . . . . . . 33-13
Test des composants non installés . . . . . 31-13 Création de méthodes pour le stockage
et le chargement de valeurs de
Chapitre 32 propriétés . . . . . . . . . . . . . . . . . . 33-13
Programmation orientée objet Redéfinition de la méthode
DefineProperties . . . . . . . . . . . . . . 33-14
et écriture des composants 32-1
Définition de nouvelles classes . . . . . . . . 32-2 Chapitre 34
Dérivation de nouvelles classes . . . . . . . . 32-2 Création d’événements 34-1
Modifier les valeurs par défaut d’une
classe pour éviter les répétitions . . . . . 32-2 Qu’est-ce qu’un événement ?. . . . . . . . . . 34-1
Ajout de nouvelles capacités à une Les événements sont des pointeurs de
classe . . . . . . . . . . . . . . . . . . . . . . 32-3 méthodes . . . . . . . . . . . . . . . . . . . . 34-2
Déclaration d’une nouvelle classe de Les événements sont des propriétés. . . . . 34-2
composant . . . . . . . . . . . . . . . . . . . . 32-3 Les types d’événements sont des types de
Ancêtres, descendants et hiérarchies des pointeurs de méthodes . . . . . . . . . . . . 34-3
classes . . . . . . . . . . . . . . . . . . . . . . . 32-4 Les types gestionnaire d’événement sont
des procédures . . . . . . . . . . . . . . . . 34-3
Contrôle des accès . . . . . . . . . . . . . . . . 32-4 Les gestionnaires d’événements sont
Masquer les détails d’implémentation . . . . 32-5 facultatifs . . . . . . . . . . . . . . . . . . . . 34-4
Définition de l’interface avec le concepteur Implémentation des événements standard . 34-5
des composants. . . . . . . . . . . . . . . . . 32-6
Identification des événements standard. . . 34-5
Définition de l’interface d’exécution . . . . . 32-6

xviii
Evénements standard pour tous les Chapitre 37
contrôles . . . . . . . . . . . . . . . . . . . . 34-5
Evénements standard pour les contrôles Gestion des messages 37-1
standard . . . . . . . . . . . . . . . . . . . . 34-5 Compréhension du système de gestion des
Rendre visibles des événements. . . . . . . . 34-6 messages . . . . . . . . . . . . . . . . . . . . . 37-1
Changement de la gestion des événements Que contient un message Windows ? . . . . 37-2
standard . . . . . . . . . . . . . . . . . . . . . 34-6 Répartition des messages . . . . . . . . . . . 37-2
Définition de vos propres événements. . . . 34-7 Suivi du flux des messages . . . . . . . . . 37-3
Déclenchement de l’événement . . . . . . . . 34-7 Modification de la gestion des messages . . 37-3
Deux sortes d’événements . . . . . . . . . . 34-7 Surcharge de la méthode du gestionnaire . 37-4
Définition du type de gestionnaire . . . . . . 34-8 Utilisation des paramètres d’un message. . 37-4
Notifications simples . . . . . . . . . . . . . 34-8 Interception des messages . . . . . . . . . . . 37-4
Gestionnaires d’événements spécifiques. . 34-8
Création de nouveaux gestionnaires de
Renvoi d’informations à partir du
gestionnaire . . . . . . . . . . . . . . . . . . 34-9 messages . . . . . . . . . . . . . . . . . . . . . 37-5
Déclaration de l’événement . . . . . . . . . . 34-9 Définition de vos propres messages. . . . . 37-5
Les noms d’événement débutent par Déclaration d’un identificateur de
“On” . . . . . . . . . . . . . . . . . . . . . . 34-9 message . . . . . . . . . . . . . . . . . . . . 37-6
Appel de l’événement. . . . . . . . . . . . . . 34-9 Déclaration d’un type enregistrement de
message . . . . . . . . . . . . . . . . . . . . 37-6
Les gestionnaires vides doivent être
valides . . . . . . . . . . . . . . . . . . . . .34-10 Déclaration d’une nouvelle méthode de
Les utilisateurs peuvent surcharger la gestion d’un message. . . . . . . . . . . . . 37-7
gestion par défaut . . . . . . . . . . . . . .34-10
Chapitre 38
Chapitre 35 Accessibilité des composants
Création de méthodes 35-1 au moment de la conception 38-1
Eviter les interdépendances . . . . . . . . . . 35-1 Recensement des composants . . . . . . . . . 38-1
Noms des méthodes. . . . . . . . . . . . . . . 35-2 Déclaration de la procédure Register . . . . 38-2
Protection des méthodes . . . . . . . . . . . . 35-3 Ecriture de la procédure Register . . . . . . 38-2
Méthodes qui doivent être publiques . . . . 35-3 Spécification des composants . . . . . . . . 38-3
Méthodes qui doivent être protégées . . . . 35-3 Spécification de la page de palette . . . . . 38-3
Méthodes abstraites . . . . . . . . . . . . . . . 35-4 Utilisation de la fonction
RegisterComponents. . . . . . . . . . . . . 38-3
Rendre virtuelles des méthodes . . . . . . . . 35-4
Ajout de bitmaps à la palette . . . . . . . . . 38-4
Déclaration des méthodes . . . . . . . . . . . 35-4
Fournir l’aide pour vos composants . . . . . 38-4
Chapitre 36 Création du fichier d’aide . . . . . . . . . . . 38-4
Création des entrées. . . . . . . . . . . . . . 38-5
Graphiques et composants 36-1 Aide contextuelle des composants . . . . . 38-6
Présentation des graphiques . . . . . . . . . . 36-1 Ajout des fichiers d’aide des
Utilisation du canevas. . . . . . . . . . . . . . 36-3 composants . . . . . . . . . . . . . . . . . . 38-7
Travail sur les images . . . . . . . . . . . . . . 36-3 Ajout d’éditeurs de propriétés . . . . . . . . . 38-7
Utilisation d’une image, d’un graphique Dérivation d’une classe éditeur de
ou d’un canevas . . . . . . . . . . . . . . . . 36-4 propriétés . . . . . . . . . . . . . . . . . . . . 38-8
Chargement et stockage des graphiques . . 36-4 Modification de la propriété sous une
forme textuelle . . . . . . . . . . . . . . . . . 38-9
Gestion des palettes . . . . . . . . . . . . . . . 36-5
Affichage de la valeur de la propriété. . . 38-9
Spécification d’une palette pour un
contrôle . . . . . . . . . . . . . . . . . . . . 36-6 Définition de la valeur de la propriété . . 38-9
Réponse aux changements de palette . . . 36-6 Modification globale de la propriété . . . 38-10
Spécification des attributs de l’éditeur . . .38-11
Bitmaps hors écran. . . . . . . . . . . . . . . . 36-6
Recensement de l’éditeur de propriétés. . 38-12
Création et gestion des bitmaps hors
écran . . . . . . . . . . . . . . . . . . . . . . . 36-6 Ajout d’éditeurs de composants. . . . . . . 38-13
Copie des images bitmap. . . . . . . . . . . . 36-7 Ajout d’éléments au menu contextuel . . 38-14
Réponse aux changements . . . . . . . . . . . 36-7 Spécification d’éléments de menu . . . . 38-14
Implémentation des commandes . . . . . 38-15

xix
Modification du comportement suite à Chapitre 41
un double-clic . . . . . . . . . . . . . . . . . 38-15
Ajout de formats de Presse-papiers . . . . 38-16 Personnalisation d’une grille 41-1
Recensement d’un éditeur de Création et recensement du composant . . . 41-1
composants . . . . . . . . . . . . . . . . . . 38-16 Publication des propriétés héritées . . . . . . 41-2
Catégories de propriété . . . . . . . . . . . . 38-17 Modification des valeurs initiales . . . . . . . 41-3
Recensement d’une propriété à la fois. . . 38-18 Redimensionnement des cellules . . . . . . . 41-4
Recensement de plusieurs propriétés en
une seule fois . . . . . . . . . . . . . . . . . 38-18 Remplissage des cellules . . . . . . . . . . . . 41-5
Classes de catégorie de propriété . . . . . . 38-19 Suivi de la date . . . . . . . . . . . . . . . . . 41-5
Catégories de propriété intégrées. . . . . .38-19 Stockage interne de la date . . . . . . . . . 41-6
Dérivation de nouvelles catégories de Accès au jour, au mois et à l’année . . . . 41-6
propriétés . . . . . . . . . . . . . . . . . . .38-20 Génération des numéros de jours . . . . . 41-8
Utilisation de la fonction Sélection du jour en cours . . . . . . . . . . 41-9
IsPropertyInCategory . . . . . . . . . . . . 38-20 Navigation de mois en mois et d’année en
Compilation des composants en paquets . 38-21 année . . . . . . . . . . . . . . . . . . . . . . 41-10
Problèmes d’installation de composants Navigation de jour en jour . . . . . . . . . . 41-11
personnalisés . . . . . . . . . . . . . . . . . 38-21 Déplacement de la sélection. . . . . . . . . .41-11
Fourniture d’un événement OnChange. . 41-12
Chapitre 39 Exclusion des cellules vides. . . . . . . . . 41-12
Modification d’un composant Chapitre 42
existant 39-1 Contrôles orientés données 42-1
Création et recensement du composant . . . 39-1 Création d’un contrôle pour scruter les
Modification de la classe composant. . . . . 39-2 données . . . . . . . . . . . . . . . . . . . . . . 42-2
Surcharge du constructeur . . . . . . . . . . . 39-2 Création et recensement du composant. . . 42-2
Spécification de la nouvelle valeur par Fonctionnement du contrôle en lecture
défaut de la propriété . . . . . . . . . . . . . 39-3 seulement . . . . . . . . . . . . . . . . . . . . 42-3
Ajout de la propriété ReadOnly . . . . . . 42-3
Chapitre 40 Autorisation des mises à jour
Création d’un composant nécessaires . . . . . . . . . . . . . . . . . . . 42-4
Ajout du lien aux données . . . . . . . . . . 42-5
graphique 40-1 Déclaration du champ de classe . . . . . . 42-5
Création et recensement du composant . . . 40-1 Déclaration des propriétés d’accès . . . . . 42-5
Publication des propriétés héritées . . . . . . 40-2 Exemple de déclaration des propriétés
d’accès . . . . . . . . . . . . . . . . . . . . . 42-6
Ajout de fonctionnalités graphiques . . . . . 40-3 Initialisation du lien de données . . . . . . 42-6
Détermination de ce qui doit être dessiné . 40-3 Réponse aux changements de données . . . 42-7
Déclaration du type de la propriété . . . . 40-3
Déclaration de la propriété . . . . . . . . . 40-4 Création d’un contrôle pour modifier les
Ecriture de la méthode d’implémen- données . . . . . . . . . . . . . . . . . . . . . . 42-8
tation . . . . . . . . . . . . . . . . . . . . . . 40-4 Modification de la valeur par défaut de
Surcharge du constructeur et du FReadOnly . . . . . . . . . . . . . . . . . . . 42-9
destructeur. . . . . . . . . . . . . . . . . . . . 40-4 Gestion des messages liés à la souris ou
Modification des valeurs par défaut des au clavier . . . . . . . . . . . . . . . . . . . . 42-9
propriétés . . . . . . . . . . . . . . . . . . . 40-4 Réponse aux messages indiquant la
Publication du crayon et du pinceau . . . . 40-5 manipulation de la souris . . . . . . . . . 42-9
Déclaration des champs de classe . . . . . 40-5 Réponse aux messages indiquant la
Déclaration des propriétés d’accès . . . . . 40-6 manipulation du clavier . . . . . . . . . 42-10
Initialisation des classes ayant un Mise à jour de la classe lien de données
propriétaire . . . . . . . . . . . . . . . . . . 40-7 sur un champ . . . . . . . . . . . . . . . . .42-11
Définition des propriétés des classes Modification de la méthode Change . . . 42-12
ayant un propriétaire . . . . . . . . . . . . 40-7 Mise à jour de l’ensemble de données . . 42-12
Dessin de l’image du composant . . . . . . . 40-8
Adaptation du dessin de la forme . . . . . . 40-9

xx
Chapitre 43 Chapitre 45
Transformation d’une boîte de Création d’un objet COM simple 45-1
dialogue en composant 43-1 Présentation de la création d’un objet
Définition de l’interface du composant . . . 43-2 COM. . . . . . . . . . . . . . . . . . . . . . . . 45-1
Création et recensement du composant . . . 43-2 Conception d’un objet COM . . . . . . . . . . 45-2
Création de l’interface du composant . . . . 43-3 Création d’un objet COM avec l’expert
objet COM . . . . . . . . . . . . . . . . . . . . 45-2
Inclusionde l’unité de la fiche . . . . . . . . . 43-3
Ajout des propriétés de l’interface . . . . . . 43-3 Types d’instanciation des objets COM . . . . 45-3
Ajout de la méthode Execute . . . . . . . . . 43-5 Choix d’un modèle de thread . . . . . . . . . 45-4
Test du composant . . . . . . . . . . . . . . . . 43-6 Ecriture d’un objet supportant le modèle
de thread libre . . . . . . . . . . . . . . . . . 45-5
Partie V Ecriture d’un objet supportant le modèle
de thread apartment . . . . . . . . . . . . . 45-6
Développement d’applications COM Recensement d’un objet COM . . . . . . . . . 45-6
Test d’un objet COM. . . . . . . . . . . . . . . 45-7
Chapitre 44
Présentation des technologies Chapitre 46
COM 44-1 Création d’un contrôleur
COM, spécification et implémentation . . 44-2 Automation 46-1
Extensions de COM. . . . . . . . . . . . . . 44-2 Création d’un contrôleur Automation en
Composantes d’une application COM . . . . 44-3 important une bibliothèque de types . . . . 46-2
Interfaces COM . . . . . . . . . . . . . . . . . . 44-3 Gestion des événements dans un
L’interface COM de base, IUnknown . . . 44-4 contrôleur Automation . . . . . . . . . . . . 46-3
Pointeurs d’interface COM . . . . . . . . . 44-5 Connexion à un serveur et déconnexion
Serveurs COM . . . . . . . . . . . . . . . . . . 44-5 d’un serveur . . . . . . . . . . . . . . . . . . 46-3
CoClasses et fabricants de classes . . . . . 44-6 Contrôle d’un serveur Automation avec
Serveurs en processus, hors processus et une interface double . . . . . . . . . . . . . 46-4
distants. . . . . . . . . . . . . . . . . . . . . 44-6 Contrôle d’un serveur Automation en
Le mécanisme du marshaling. . . . . . . . 44-8 utilisant interface de répartition . . . . . . 46-4
Clients COM . . . . . . . . . . . . . . . . . . . 44-9 Exemple : impression d’un document dans
Extensions de COM . . . . . . . . . . . . . . . 44-9 Microsoft Word . . . . . . . . . . . . . . . . 46-5
Serveurs et contrôleurs Automation . . . . 44-12 Etape 1 : Préparation de Delphi pour cet
Contrôles ActiveX . . . . . . . . . . . . . . . 44-12 exemple . . . . . . . . . . . . . . . . . . . . 46-5
Etape 2 : importation de la bibliothèque
Bibliothèques de types . . . . . . . . . . . . 44-13 de types Word . . . . . . . . . . . . . . . . 46-5
Contenu d’une bibliothèque de types . . .44-13 Etape 3 : utilisation d’un objet interface
Création de bibliothèques de types . . . .44-14 VTable ou de répartition pour contrôler
Quand utiliser les bibliothèques de Microsoft Word . . . . . . . . . . . . . . . . 46-6
types . . . . . . . . . . . . . . . . . . . . . .44-14 Etape 4 : Nettoyage de l’exemple . . . . . . 46-7
Accès aux bibliothèques de types . . . . .44-15 Autres sources d’informations . . . . . . . . 46-8
Avantages des bibliothèques de types. . .44-15
Utilisation des outils de bibliothèques Chapitre 47
de types . . . . . . . . . . . . . . . . . . . .44-16
Pages Active Server . . . . . . . . . . . . . . 44-16 Création d’un serveur Automation 47-1
Documents Active . . . . . . . . . . . . . . . 44-17 Création d’un objet Automation pour une
Objets visuels inter-processus . . . . . . . . 44-17 application . . . . . . . . . . . . . . . . . . . . 47-1
Implémentation des objets COM à l’aide Gestion des événements de l’objet
d’experts . . . . . . . . . . . . . . . . . . . . 44-18 Automation. . . . . . . . . . . . . . . . . . . . 47-3

xxi
Exposition des propriétés, méthodes et Création d’une page de propriétés pour
événements d’une application . . . . . . . . 47-3 un contrôle ActiveX . . . . . . . . . . . . . 48-14
Exposition d’une propriété à l’Automation . 47-3 Création d’une nouvelle page de
Exposition d’une méthode à l’Automation . 47-4 propriétés . . . . . . . . . . . . . . . . . . . 48-15
Exposition d’un événement à Ajout des contrôles à une page de
l’Automation . . . . . . . . . . . . . . . . . . 47-4 propriétés . . . . . . . . . . . . . . . . . . . 48-15
Autres sources d’informations . . . . . . . . . 47-5 Association des contrôles de la page de
Recensement d’une application comme propriétés aux propriétés du contrôle
serveur Automation . . . . . . . . . . . . . . 47-6 ActiveX . . . . . . . . . . . . . . . . . . . . 48-16
Recensement d’un serveur en processus . . 47-6 Actualisation de la page de propriétés . 48-16
Actualisation de l’objet . . . . . . . . . . . 48-16
Recensement d’un serveur hors processus . 47-6
Connexion d’une page de propriétés à
Test et débogage de l’application . . . . . . . 47-6 un contrôle ActiveX. . . . . . . . . . . . . 48-17
Interfaces Automation . . . . . . . . . . . . . . 47-7 Exposition des propriétés d’un contrôle
Interfaces duales . . . . . . . . . . . . . . . . . 47-7 ActiveX . . . . . . . . . . . . . . . . . . . . . 48-17
Interfaces de répartition. . . . . . . . . . . . . 47-8 Recensement d’un contrôle ActiveX . . . . 48-18
Interfaces personnalisées . . . . . . . . . . . . 47-9
Test d’un contrôle ActiveX . . . . . . . . . . 48-19
Marshaling des données . . . . . . . . . . . . 47-9
Déploiement d’un contrôle ActiveX sur le
Types compatibles avec l’Automation . . . . 47-9
Web . . . . . . . . . . . . . . . . . . . . . . . 48-19
Restrictions de type pour le marshaling
automatique . . . . . . . . . . . . . . . . . . 47-10 Paramétrage des options de déploiement
Web. . . . . . . . . . . . . . . . . . . . . . . 48-20
Marshaling personnalisé . . . . . . . . . . . 47-10
Case à cocher Défaut des Options de
déploiement Web. . . . . . . . . . . . . . 48-21
Chapitre 48 Fichier .INF . . . . . . . . . . . . . . . . . . 48-21
Création d’un contrôle ActiveX 48-1 Combinaisons d’options . . . . . . . . . . 48-22
Présentation de la création d’un contrôle Page Projet . . . . . . . . . . . . . . . . . . . 48-22
ActiveX . . . . . . . . . . . . . . . . . . . . . . 48-1 Page Paquets . . . . . . . . . . . . . . . . . . 48-23
Eléments d’un contrôle ActiveX. . . . . . . . 48-2 Paquets utilisés par ce projet . . . . . . . 48-24
Contrôle VCL. . . . . . . . . . . . . . . . . . 48-2 Options CAB . . . . . . . . . . . . . . . . . 48-24
Bibliothèque de types. . . . . . . . . . . . . 48-3 Options de sortie. . . . . . . . . . . . . . . 48-24
Propriétés, méthodes et événements . . . . 48-3 Options de répertoire et URL . . . . . . . 48-24
Page de propriétés. . . . . . . . . . . . . . . 48-3 Page Fichiers supplémentaires . . . . . . . 48-24
Fichiers associés au projet . . . . . . . . . 48-25
Conception d’un contrôle ActiveX . . . . . . 48-3
Options CAB . . . . . . . . . . . . . . . . . 48-25
Génération d’un contrôle ActiveX à partir Options de sortie. . . . . . . . . . . . . . . 48-25
d’un contrôle VCL . . . . . . . . . . . . . . . 48-4 Options de répertoire et URL . . . . . . . 48-25
Définition des licences des contrôles Page Encodage. . . . . . . . . . . . . . . . . 48-25
ActiveX . . . . . . . . . . . . . . . . . . . . . . 48-6 Informations requises . . . . . . . . . . . . 48-26
Génération d’un contrôle ActiveX basé sur Informations facultatives . . . . . . . . . . 48-26
une fiche VCL. . . . . . . . . . . . . . . . . . 48-7 Serveur de date de validité . . . . . . . . 48-26
Manipulation des propriétés, méthodes et Algorithme cryptographique. . . . . . . . 48-27
événements d’un contrôle ActiveX . . . . . 48-9
Ajout de propriétés, méthodes et Chapitre 49
événements supplémentaires. . . . . . . . . 48-9 Utilisation des bibliothèques de
Comment Delphi ajoute les propriétés . .48-10
Comment Delphi ajoute les méthodes . .48-11 types 49-1
Comment Delphi ajoute les L’éditeur de bibliothèques de types. . . . . . 49-2
événements . . . . . . . . . . . . . . . . . .48-11 Barre d’outils . . . . . . . . . . . . . . . . . . . 49-3
Activation de la liaison de données Volet liste des objets . . . . . . . . . . . . . . 49-5
simple avec la bibliothèque de types. . . 48-12 Barre d’état . . . . . . . . . . . . . . . . . . . . 49-6
Activation de la liaison de données Les pages d’informations de type . . . . . . 49-6
simple des contrôles ActiveX dans le Page Attributs . . . . . . . . . . . . . . . . . 49-6
conteneur Delphi . . . . . . . . . . . . . . . 48-13 Page Texte . . . . . . . . . . . . . . . . . . . . 49-7
Pages Indicateurs. . . . . . . . . . . . . . . . 49-7

xxii
Informations de type d’une bibliothèque . . 49-7 Syntaxe pour une énumération . . . . . . 49-29
Page Attributs d’une bibliothèque de Syntaxe pour un alias . . . . . . . . . . . . 49-30
types . . . . . . . . . . . . . . . . . . . . . . . 49-8 Syntaxe pour un enregistrement . . . . . 49-30
Page Utilise d’une bibliothèque de types . . 49-8 Syntaxe pour une union . . . . . . . . . . 49-30
Page Indicateurs d’une bibliothèque de Syntaxe pour un module . . . . . . . . . . 49-31
types . . . . . . . . . . . . . . . . . . . . . . . 49-8 Création d’une nouvelle bibliothèque de
Pages d’une interface . . . . . . . . . . . . . . 49-9 types . . . . . . . . . . . . . . . . . . . . . . 49-31
Page Attributs d’une interface. . . . . . . . . 49-9 Ouverture d’une bibliothèque de types
Indicateurs d’une interface . . . . . . . . . . . 49-9 existante . . . . . . . . . . . . . . . . . . . . 49-32
Membres d’une interface . . . . . . . . . . . 49-10 Ajout d’une interface à une bibliothèque
de types . . . . . . . . . . . . . . . . . . . . 49-32
Méthodes d’une interface . . . . . . . . . .49-10
Propriétés d’une interface . . . . . . . . . .49-11 Ajout de propriétés et méthodes à une
interface ou dispinterface . . . . . . . . . 49-33
Page Paramètres des propriétés et
méthodes . . . . . . . . . . . . . . . . . . .49-12 Ajout d’une CoClasse à une bibliothèque
de types . . . . . . . . . . . . . . . . . . . . 49-34
Informations de type d’une interface de Ajout d’une énumération à une
répartition . . . . . . . . . . . . . . . . . . . 49-14 bibliothèque de types. . . . . . . . . . . . 49-34
Page Attributs d’une interface de Enregistrement et recensement des
répartition . . . . . . . . . . . . . . . . . . . 49-15 informations d’une bibliothèque de
Page Indicateurs d’une interface de types . . . . . . . . . . . . . . . . . . . . . . 49-34
répartition . . . . . . . . . . . . . . . . . . . 49-15 Boîte de dialogue Appliquer les mises
Membres d’une interface de répartition . . 49-15 à jour . . . . . . . . . . . . . . . . . . . . . 49-35
Pages d’une CoClasse . . . . . . . . . . . . . 49-16 Enregistrement d’une bibliothèque de
Page Attributs d’une CoClasse . . . . . . . 49-16 types . . . . . . . . . . . . . . . . . . . . . 49-36
Page Implémente d’une CoClasse . . . . . 49-17 Rafraîchissement de la bibliothèque de
Indicateurs d’une CoClasse . . . . . . . . . 49-17 types . . . . . . . . . . . . . . . . . . . . . 49-36
Recensement d’une bibliothèque de
Informations de type d’une énumération . 49-18 types . . . . . . . . . . . . . . . . . . . . . 49-36
Page Attributs d’une énumération . . . . . 49-18 Exportation d’un fichier IDL. . . . . . . . 49-37
Membres d’une énumération . . . . . . . . 49-19
Déploiement des bibliothèques de types . 49-37
Informations de type d’un alias. . . . . . . 49-19
Page Attributs d’un alias . . . . . . . . . . . 49-19 Chapitre 50
Informations de type d’un Création des objets MTS 50-1
enregistrement. . . . . . . . . . . . . . . . . 49-20
Page Attributs d’un enregistrement . . . . 49-20 Composants Microsoft Transaction Server. . 50-2
Membres d’un enregistrement. . . . . . . . 49-20 Exigences d’un composant MTS . . . . . . . 50-4
Informations de type d’une union . . . . . 49-21 Gestion des ressources avec l’activation
Page Attributs d’une union . . . . . . . . . 49-21 just-in-time et le pooling . . . . . . . . . . . 50-5
Membres d’une union. . . . . . . . . . . . . 49-21 Activation just-in-time . . . . . . . . . . . . . 50-5
Informations de type d’un module. . . . . 49-22 Pooling des ressources . . . . . . . . . . . . . 50-6
Page Attributs d’un module . . . . . . . . . 49-22 Libération des ressources . . . . . . . . . . . 50-6
Membres d’un module . . . . . . . . . . . . 49-22 Pooling des objets . . . . . . . . . . . . . . . . 50-6
Méthodes d’un module. . . . . . . . . . . .49-23 Accès au contexte d’un objet . . . . . . . . . 50-7
Constantes de module . . . . . . . . . . . .49-23 Support transactionnel MTS . . . . . . . . . . 50-7
Création de nouvelles bibliothèques de Attributs transactionnels . . . . . . . . . . . . 50-8
types. . . . . . . . . . . . . . . . . . . . . . . 49-23 Le contexte d’objet contient l’attribut de
Types autorisés . . . . . . . . . . . . . . . . . 49-24 la transaction . . . . . . . . . . . . . . . . . . 50-9
Les SafeArray. . . . . . . . . . . . . . . . . .49-25 Objets avec état et sans état. . . . . . . . . . 50-9
Utilisation de la syntaxe Pascal Objet ou Activation de plusieurs objets pour
IDL . . . . . . . . . . . . . . . . . . . . . . . 49-25 supporter les transactions . . . . . . . . . 50-10
Spécifications des attributs. . . . . . . . . .49-26 Transactions contrôlées par MTS ou par
Syntaxe pour une interface . . . . . . . . .49-27 le client . . . . . . . . . . . . . . . . . . . . 50-10
Syntaxe pour une interface de Avantage des transactions . . . . . . . . . . .50-11
répartition . . . . . . . . . . . . . . . . . . .49-28 Temporisation des transactions . . . . . . . .50-11
Syntaxe pour une CoClasse . . . . . . . . .49-29 Sécurité en fonction des rôles . . . . . . . . 50-12

xxiii
Dispenseurs de ressources . . . . . . . . . . 50-12 Définition d’un objet transaction côté
Dispenseur de ressources BDE . . . . . . . 50-13 client . . . . . . . . . . . . . . . . . . . . . . . 50-21
Gestionnaire de propriétés partagées. . . . 50-13 Définition d’un objet transaction côté
Exemple : Partage de propriétés entre serveur . . . . . . . . . . . . . . . . . . . . . 50-22
les instances d’un objet MTS . . . . . . .50-13 Débogage et test des objets MTS . . . . . . 50-22
Conseils d’utilisation du gestionnaire de
propriétés partagées. . . . . . . . . . . . .50-14 Installation des objets MTS dans un
paquet MTS . . . . . . . . . . . . . . . . . . 50-23
Clients de base et composants MTS . . . . 50-15
Administration des objets MTS avec
Technologies sous-jacentes de MTS, l’explorateur MTS. . . . . . . . . . . . . . . 50-24
COM et DCOM. . . . . . . . . . . . . . . . 50-16
Utilisation de la documentation MTS . . . 50-24
Présentation de la création des objets
MTS . . . . . . . . . . . . . . . . . . . . . . . 50-16 Chapitre 51
Utilisation de l’expert objet MTS . . . . . . 50-16
Choix d’un modèle de thread pour un Création d’une page Active Server 51-1
objet MTS . . . . . . . . . . . . . . . . . . . 50-17 Création d’un objet ASP . . . . . . . . . . . . 51-2
Activités MTS . . . . . . . . . . . . . . . . . . 50-18 Création d’ASP pour des serveurs en et
Définition de l’attribut transactionnel . . . 50-19 hors processus . . . . . . . . . . . . . . . . . 51-3
Transmission de références à des objets . . 50-20 Recensement d’une application comme
Utilisation de la méthode SafeRef . . . . .50-20 objet ASP . . . . . . . . . . . . . . . . . . . . . 51-4
Callbacks . . . . . . . . . . . . . . . . . . . .50-21 Recensement d’un serveur en processus . . 51-4
Recensement d’un serveur hors processus . 51-4
Test et débogage d’une application ASP. . . 51-5

xxiv
Chapitre

1
Introduction
Chapter 1

Ce manuel aborde des notions de développement intermédiaires et avancées. Il


traite notamment de la création d’applications de bases de données
client/serveur, de l’écriture de composants personnalisés, de la création
d’applications serveurs Web Internet et de la gestion de spécifications reconnues
comme TCP/IP, OLE ou ActiveX. Ce guide suppose que l’utilisation et les
techniques fondamentales de programmation Delphi ont été assimilées. Pour une
présentation de la programmation Delphi et de l’environnement de
développement intégré (EDI), voir l’aide en ligne.

Contenu de ce manuel
Ce manuel comporte cinq parties décomposées comme suit :
• La partie I, “Programmation avec Delphi,” décrit la manière de concevoir des
applications Delphi généralistes. Cette partie donne des détails sur les
techniques de programmation utilisables dans toute application Delphi. Elle
décrit, par exemple, la manière d’utiliser les objets courants de la bibliothèque
de composants visuels qui simplifient la programmation de l’interface
utilisateur comme la gestion de chaînes, la manipulation de texte,
l’implémentation des boîtes de dialogue standard Windows et les barres
d’outils. Elle contient également des chapitres décrivant la manipulation des
graphiques et la gestion des erreurs et des exceptions, l’utilisation des DLL,
l’automation OLE et l’écriture d’applications internationales.
Le chapitre sur le déploiement aborde les opérations nécessaires pour
distribuer votre application auprès de ses utilisateurs. Ce chapitre donne des
informations sur les options de compilation, l’utilisation de InstallShield
Express, les problèmes de droits de distribution et sur la manière de
déterminer les paquets, DLL et autres bibliothèques qu’il faut utiliser pour
générer la version distribuée d’une application.

Introduction 1-1
Contenu de ce manuel

• La partie II, “Développement d’applications de base de données,” décrit la


manière de concevoir des applications de base de données en utilisant les
outils et composants de données. Delphi permet d’accéder à divers types de
base de données. A l’aide de fiches et d’états, vous pouvez accéder à des
bases de données locales comme Paradox ou dBASE, à des serveurs de bases
de données SQL en réseau comme InterBase ou Sybase et à toute source de
données accessible via ODBC. Pour pouvoir créer des applications de base de
données Client/Serveur sophistiquées, vous devez disposer de caractéristiques
disponibles dans les versions Client/Serveur ou Entreprise.
• La partie III, “Ecriture d’applications distribuées,” décrit comment créer des
applications distribuées sur un réseau local. Ces sont les applications CORBA
ou les serveurs Web (applications CGI et les DLL NSAPI ou ISAPI). Pour la
gestion de bas niveau des applications distribuées, cette partie décrit
également l’utilisation des composants socket qui gèrent le détail des
communications en utilisant TCP/IP et d’autres protocoles associés. Les
composants gérant les applications CORBA et serveur Web sont proposés dans
les versions Client/Serveur et Entreprise de Delphi. Le composants socket est
également proposé par la version professionnelle.
• La partie IV, “Création de composants personnalisés,” décrit la manière de
concevoir et d’implémenter vos propres composants et de les intégrer à la
palette des composants de l’EDI. Un composant peut quasiment constituer
tout élément de programme manipulable à la conception. L’implémentation de
composants personnalisés nécessite la dérivation d’une nouvelle classe à partir
d’une classe existante dans la bibliothèque de classes VCL.
• La partie V, “Développement d’applications COM,” décrit la manière de
concevoir des applications qui interagissent avec d’autres objets du système
basés sur l’API COM comme les extensions du Shell Win95 ou des
applications multimédia. Delphi dispose de composants gérant la bibliothèque
ActiveX basée sur COM pour les contrôles COM qui peuvent s’utiliser dans
des applications standard ou dans des applications Web. Cette partie décrit
également comment concevoir des serveurs placés dans l’environnement
d’exécution MTS. MTS fournit à l’exécution une gestion extensive de la
sécurité, des transactions et de la mise en commun des ressources.
La gestion des contrôles COM est proposée dans toutes les versions de Delphi.
Pour créer des contrôles ActiveX, vous avez besoin de la version
professionnelle, Client/Serveur ou Entreprise. Pour créer des serveurs MTS,
vous avez besoin de la version Client/Serveur ou Entreprise.

1-2 Guide du développeur


Conventions typographiques

Conventions typographiques
Ce manuel utilise les polices et les symboles décrits dans le tableau suivant pour
mettre en évidence des parties particulières du texte :

Tableau 1.1 Polices et symboles utilisés dans ce manuel


Police ou symbole Signification
Police à pas fixe Le texte apparaissant avec une police à pas fixe sert à représenter le
texte tel qu’il apparaît à l’écran ou dans du code Pascal Objet. Il
indique aussi les valeurs à saisir au clavier.
[] Les crochets dans le texte ou dans une syntaxe représentent des
éléments facultatifs. Ces crochets sont à omettre lors de la saisie.
Gras Les mots en gras dans le texte ou dans le code servent à représenter les
mots réservés du langage Pascal Objet et les options du compilateur.
Italique Les mots en caractères italiques représentent des identificateurs du
langage Pascal Objet comme les variables et les noms de types.
L’italique sert aussi à faire ressortir certains mots comme les nouveaux
termes.
Touches Cette police sert à indiquer une touche du clavier. Par exemple,
“Appuyez sur Echap pour quitter un menu”.

Support technique Inprise


Inprise offre également de nombreuses options de support pour satisfaire les
besoins de l’ensemble de ses développeurs. Pour obtenir des informations sur les
services de support Delphi, visitez le site Web à l’adresse www.inprise.fr.
Des documents d’informations techniques Delphi supplémentaires et des
réponses aux questions les plus fréquentes sont disponibles sur le site Web à
l’adresse http://www.borland.com/devsupport/delphi.
A partir de ce site Web, vous pouvez également accéder à de nombreux groupes
de news où les développeurs Delphi s’échangent des informations, des astuces et
des techniques. Le site comprend également une liste d’ouvrages sur Delphi.

Introduction 1-3
1-4 Guide du développeur
Partie

I
Programmation avec Delphi
Part I

Les chapitres de cette section présentent les concepts et les connaissances


nécessaires à la création d’applications Delphi à l’aide de n’importe quelle édition
du produit. Ils introduisent également les concepts traités dans les dernières
sections du guide du développeur.

Programmation avec Delphi


Chapitre

Utilisation du Pascal Objet


Chapter 2
2
avec la VCL
Ce chapitre décrit la manière d’utiliser le Pascal Objet avec la bibliothèque
d’objets et de composants (VCL) dans les applications Delphi.

Pascal Objet et VCL


Le Pascal Objet qui est constitué d’un ensemble d’extensions orientées objet du
Pascal standard est le langage de Delphi. La bibliothèque de composants visuels
(VCL) est une hiérarchie de classes, écrites en Pascal Objet et liées à l’EDI Delphi,
qui permet de développer rapidement des applications. En utilisant la palette des
composants Delphi et l’inspecteur d’objet, vous pouvez placer des composants
VCL dans des fiches et manipuler leurs propriétés sans écrire de code.
Tous les objets VCL descendent de TObject qui est une classe abstraite dont les
méthodes encapsulent des comportements essentiels comme la construction, la
destruction et la gestion des messages. TObject est l’ancêtre immédiat de
nombreuses classes simples.
Les composants de la VCL descendent de la classe abstraite TComponent. Les
composants sont des objets que vous pouvez manipuler dans les fiches à la
conception. Les composants visuels, c’est-à-dire ceux qui comme TForm ou
TSpeedButton apparaissent à l’écran lors de l’exécution, sont appelés des contrôles
et descendent de TControl.
En dépit de son nom, la VCL est essentiellement constituée d’objets non-visuels.
L’EDI Delphi vous permet d’ajouter de nombreux composants non-visuels à vos
programmes en les déposant dans les fiches. Si, par exemple, vous écrivez une
application qui se connecte à une base de données, vous pouvez placer un
composant TDataSource dans une fiche. Même si TDataSource est non-visuel, il est

Utilisation du Pascal Objet avec la VCL 2-1


Utilisation du modèle objet

représenté dans la fiche par une icône qui n’apparaît pas à l’exécution. Vous
pouvez manipuler les propriétés et événements de TDataSource à l’aide de
l’inspecteur d’objet comme pour un contrôle visuel.
Quand vous écrivez vos propres classes en Pascal Objet, elles doivent descendre
de TObject. En dérivant de nouvelles classes de la classe de base de la VCL (ou
de l’un de ses descendants), vous fournissez à vos classes des fonctionnalités
essentielles tout en garantissant qu’elles peuvent fonctionner avec la VCL.

Utilisation du modèle objet


La programmation orientée objet (POO) est une extension de la programmation
structurée qui intensifie la réutilisation du code et l’encapsulation de données
avec des fonctionnalités. Quand vous avez créé un objet (ou, plus précisément,
une classe), vous et d’autres programmeurs pouvez l’utiliser dans d’autres
applications ce qui réduit les temps de développement et accroît la productivité.
Pour créer de nouveaux composants et les placer dans la palette des composants
Delphi, voir chapitre 31, “Présentation générale de la création d’un composant.”

Qu’est-ce qu’un objet ?


Un objet, ou classe, est un type de données qui regroupe des données et des
opérations sur ces données. Avant la programmation orientée objet, les données et
les opérations (les fonctions) constituaient des éléments distincts.
Vous pouvez comprendre les objets si vous comprenez les enregistrements Pascal
Objet. Les enregistrements (analogues aux structures en C) sont constitués de
champs qui contiennent des données, chaque champ ayant son propre type. Les
enregistrements sont un moyen commode de désigner une collection éléments de
données variés.
Les objets sont également des collections d’éléments de données. Mais les objets,
à la différence des enregistrements, contiennent des procédures et fonctions
portant sur leurs données. Ces procédures et fonctions sont appelées des
méthodes.
Les éléments de données d’un objet sont accessibles via des propriétés. Les
propriétés des objets Delphi ont une valeur qu’il est possible de modifier à la
conception sans écrire de code. Si vous voulez modifier la valeur d’une propriété
à l’exécution, il vous suffit d’écrire un minimum de code.
La combinaison des données et de fonctionnalités dans un seul élément est
appelée encapsulation. Outre l’encapsulation, la programmation orientée objet est
caractérisée par l’héritage et le polymorphisme. Héritage signifie que les objets
dérivent leurs fonctionnalités d’autres objets appelés ancêtres) ; les objets peuvent
modifier leurs comportements hérités. Polymorphisme signifie que différents
objets qui dérivent d’un même ancêtre gèrent la même interface de méthode et
de propriété, on dit aussi qu’ils sont interchangeables.

2-2 Guide du développeur


Utilisation du modèle objet

Examen d’un objet Delphi


Quand vous créez un nouveau projet, Delphi affiche une nouvelle fiche que vous
pouvez personnaliser. Dans l’éditeur de code, Delphi déclare un nouveau type
de classe pour la fiche et génère le code qui crée la nouvelle instance de fiche.
Le code généré a la forme suivante :
unit Unit1;
interface
uses Windows, Classes, Graphics, Forms, Controls, Apps;
type
TForm1 = class(TForm){ La déclaration de type de la fiche commence ici }
private
{ Déclarations privées }
public
{ Déclarations publiques }
end;{ La déclaration de type de la fiche s’arrête ici }
var
Form1: TForm1;
implementation { Début de la partie implémentation }
{$R *.DFM}
end.{ Fin de la partie implémentation et de l’unité}
Le nouveau type de classe est TForm1, il dérive du type TForm qui est également
une classe.
Une classe ressemble à un enregistrement car tous les deux contiennent des
champs de données, mais une classe contient également des méthodes (du code
agissant sur les données de l’objet). Pour le moment, TForm1 semble ne contenir
ni champs, ni méthodes car vous n’avez ajouté à la fiche aucun composant (les
champs du nouvel objet) et vous n’avez pas créé de gestionnaires d’événements
(les méthodes du nouvel objet). TForm1 contient les champs et méthodes hérités
même s’ils n’apparaissent pas dans la déclaration de type.
La déclaration de variable suivante déclare une variable nommée Form1 ayant le
nouveau type TForm1.
var
Form1: TForm1;
Form1 représente une instance, ou objet, du type de classe TForm1. Vous pouvez
déclarer plusieurs instances d’un type de classe ; par exemple, pour créer
plusieurs fenêtres enfant dans une application utilisant l’interface d’une
application à documents multiples (MDI). Chaque instance a ses données
propres, mais elle utilise le même code pour exécuter les méthodes.
Même si vous n’avez ajouté aucun composant à la fiche ni écrit de code, vous
disposez déjà d’une application Delphi que vous pouvez compiler et exécuter.
Elle se contente d’afficher une fiche vide.

Utilisation du Pascal Objet avec la VCL 2-3


Utilisation du modèle objet

Vous pouvez alors ajouter un composant bouton à la fiche et écrire un


gestionnaire d’événement OnClick qui modifie la couleur de la fiche quand
l’utilisateur clique sur le bouton. Le résultat a l’aspect suivant :
Figure 2.1 Une fiche simple

Quand l’utilisateur clique sur le bouton, la couleur de la fiche passe au vert.


Voici le code du gestionnaire d’événement pour l’événement OnClick du bouton :
procedure TForm1.Button1Click(Sender: TObject);
begin
Form1.Color := clGreen;
end;
Un objet peut contenir d’autres objets dans ses champs de données. A chaque
fois que vous placez un composant dans une fiche, un nouveau champ apparaît
dans la déclaration de type de la fiche. Si vous créez l’application qui vient
d’être décrite, et examinez le code dans l’éditeur de code, vous trouvez :
unit Unit1;
interface
uses Windows, Classes, Graphics, Forms, Controls, Apps;
type
TForm1 = class(TForm)
Button1: TButton;{ Nouveau champ de données}
procedure Button1Click(Sender: TObject);{ Nouvelle déclaration de méthode }
private
{ Déclarations privées }
public
{ Déclarations publiques }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}

2-4 Guide du développeur


Utilisation du modèle objet

procedure TForm1.Button1Click(Sender: TObject);{ Le code de la nouvelle méthode }


begin
Form1.Color := clGreen;
end;
end.
TForm1 contient un champ Button1 qui correspond au bouton ajouté dans la
fiche. TButton est un type de classe, donc Button1 désigne un objet.
Tous les gestionnaires d’événements que vous écrivez dans Delphi sont des
méthodes de l’objet fiche. A chaque fois que vous créez un gestionnaire
d’événement, une méthode est déclarée dans le type de l’objet fiche. Le type
TForm1 contient maintenant une nouvelle méthode, la procédure Button1Click,
déclarée dans la déclaration de type de TForm1. Le code qui implémente la
méthode Button1Click apparaît dans la partie implementation de l’unité.

Modification du nom d’un composant


Vous devez toujours utiliser l’inspecteur d’objet pour modifier le nom d’un
composant. Par exemple, vous pouvez changer le nom par défaut de la fiche
Form1 pour lui donner un nom plus parlant, comme ColorBox. Quand vous
modifiez la valeur de la propriété Name de la fiche dans l’inspecteur d’objet, le
nouveau nom est automatiquement reflété dans le fichier .DFM de la fiche (que
vous ne devez généralement pas modifier directement) et dans le code source
Pascal Objet généré par Delphi :
unit Unit1;
interface
uses Windows, Classes, Graphics, Forms, Controls, Apps;
type
TColorBox = class(TForm){ remplacement de TForm1 par TColorBox }
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Déclarations privées }
public
{ Déclarations publiques }
end;
var
ColorBox: TColorBox;{ Remplacement de Form1 par ColorBox }
implementation
{$R *.DFM}
procedure TColorBox.Button1Click(Sender: TObject);
begin
Form1.Color := clGreen;{ La référence à Form1 n’a pas été actualisée! }
end;
end.

Utilisation du Pascal Objet avec la VCL 2-5


Utilisation du modèle objet

Remarquez que le code du gestionnaire d’événement OnClick du bouton n’a pas


été modifié. Comme c’est vous qui avez écrit le code, c’est aussi à vous de le
modifier et de rectifier toute référence à la fiche :
procedure TColorBox.Button1Click(Sender: TObject);
begin
ColorBox.Color := clGreen;
end;

Héritage des données et du code d’un objet


L’objet TForm1 fiche décrit à la page 2-3 semble simple. TForm1 semble ne
contenir qu’un seul champ (Button1), une méthode (Button1Click) et aucune
propriété. Vous pouvez néanmoins afficher, masquer et redimensionner la fiche,
ajouter ou retirer les icônes standard en haut de la fiche ou la configurer pour
qu’elle fasse partie d’une application à interface de document multiple (MDI).
Vous pouvez faire tout cela car la fiche a hérité de toutes les propriétés et
méthodes du composant VCL TForm. Quand vous ajoutez une nouvelle fiche à
votre projet, vous partez de TForm que vous personnalisez en lui ajoutant des
composants, en modifiant la valeur des propriétés et en écrivant des
gestionnaires d’événements. Pour personnaliser un objet, il faut d’abord dériver
un nouvel objet d’un objet existant ; quand vous ajoutez une nouvelle fiche à un
projet, Delphi dérive automatiquement une nouvelle fiche du type TForm :
TForm1 = class(TForm)
Un objet dérivé hérite de toutes les propriétés, événements et méthodes de l’objet
dont il dérive. L’objet dérivé est appelé un descendant et l’objet dont il dérive est
appelé son ancêtre. Si vous recherchez TForm dans l’aide en ligne, vous trouverez
la liste de ses propriétés, événements et méthodes, y compris ceux que TForm a
hérité de ses ancêtres. Un objet ne peut avoir qu’un seul ancêtre immédiat, mais
il peut avoir plusieurs descendants directs.

Objets, composants et contrôles


Figure 2.2 Diagramme simplifié de la hiérarchie VCL

TObject

TComponent

TControl

TForm TButton TCheckBox TListBox

2-6 Guide du développeur


Utilisation du modèle objet

Le diagramme ci-dessus est une vue, très simplifiée, de la hiérarchie des


héritages dans la bibliothèque de composants visuels. Chaque objet hérite de
TObject et beaucoup d’objets héritent de TComponent. Les contrôles, qui héritent
de TControl, ont la capacité à s’afficher eux-mêmes à l’exécution. Un contrôle
comme TCheckBox hérite de toutes les fonctionnalités de TObject, TComponent et
de TControl, et ajoute ses spécificités propres.

Portée et qualificateurs
La portée détermine l’accessibilité des champs, propriétés et méthodes d’un objet.
Tous les membres déclarés par un objet sont accessibles dans l’objet et dans ses
descendants. Même si le code d’implémentation d’une méthode apparaît hors de
la déclaration de l’objet, la méthode reste dans la portée de l’objet car elle est
déclarée dans la déclaration de l’objet.
Quand vous écrivez du code pour implémenter une méthode qui désigne les
propriétés, méthodes ou champs de l’objet dans lequel la méthode est déclarée,
vous n’avez pas besoin de préfixer ces identificateurs avec le nom de l’objet. Si,
par exemple, vous placez un bouton dans une nouvelle fiche, vous pouvez écrire
le gestionnaire d’événement suivant pour l’événement OnClick du bouton :
procedure TForm1.Button1Click(Sender: TObject);
begin
Color := clFuchsia;
Button1.Color := clLime;
end;
La première instruction est équivalente à :
Form1.Color := clFuchsia
Il n’est pas nécessaire de qualifier Color avec Form1 car la méthode Button1Click
fait partie de TForm1 ; les identificateurs placés dans la méthode sont alors dans
la portée de l’instance de TForm1 où la méthode a été déclarée. Par contre, la
seconde instruction désigne la couleur de l’objet bouton, pas celle de la fiche dans
laquelle le gestionnaire d’événement est déclaré, elle doit donc être qualifiée.
Delphi crée un fichier unité séparé (du code source) pour chaque fiche. Si vous
souhaitez accéder aux composants d’une fiche depuis le fichier unité d’une autre
fiche, vous devez qualifier les noms de composants de la manière suivante :
Form2.Edit1.Color := clLime;
De la même manière, vous pouvez accéder aux méthodes d’un composant d’une
autre fiche, par exemple :
Form2.Edit1.Clear;
Pour accéder aux composants de Form2 depuis le ficher unité de Form1, vous
devez également ajouter l’unité de Form2 à la clause uses de l’unité de Form1.
La portée d’un objet s’étend aux descendants de l’objet. Vous pouvez néanmoins
redéclarer un champ, une propriété ou une méthode dans un objet descendant.
De telles redéclarations masquent ou surchargent le membre hérité.
Pour davantage d’informations sur la portée, l’héritage et la clause uses, voir le
Guide du langage Pascal Objet.

Utilisation du Pascal Objet avec la VCL 2-7


Utilisation du modèle objet

Déclarations privées, protégées, publiques et publiées


Quand vous déclarez un champ, une propriété ou une méthode, la visibilité du
nouveau membre est indiquée par l’un des mots-clés suivants : private,
protected, public ou published. La visibilité d’un membre détermine son
accessibilité par d’autres objets et unités.
• Un membre privé (private) est accessible uniquement dans l’unité dans
laquelle il a été déclaré. Les membres privés sont souvent utilisés dans une
classe pour implémenter d’autres méthodes et propriétés (publiques ou
publiées).
• Un membre protégé (protected) est accessible dans l’unité dans laquelle la
classe est déclarée et dans toute classe descendante, quelle que soit l’unité de
la classe du descendant.
• Un membre public (public) est accessible partout où l’objet auquel il
appartient est accessible, c’est-à-dire dans l’unité dans laquelle la classe est
déclarée et dans toute unité utilisant cette unité.
• Un membre publié (published) a la même visibilité qu’un membre public
mais le compilateur génère des informations de type à l’exécution pour les
membres publiés. Les propriétés publiées apparaissent dans l’inspecteur
d’objet à la conception.
Pour davantage d’informations sur la visibilité, voir le Guide du langage Pascal
Objet.

Utilisation de variables objet


Vous pouvez affecter une variable objet à une autre variable objet si ces variables
sont de même type ou de types compatibles pour l’affectation. En particulier,
vous pouvez affecter une variable objet à une autre variable objet si le type de la
variable affectée est un ancêtre du type de la variable qui est affectée. Par
exemple, voici la déclaration du type TDataForm et une section déclaration de
variables dans laquelle deux variables sont déclarées, AForm et DataForm:
type
TDataForm = class(TForm)
Button1: TButton;
Edit1: TEdit;
DataGrid1: TDataGrid;
Database1: TDatabase;
private
{ Déclarations privées }
public
{ Déclarations publiques }
end;
var
AForm: TForm;
DataForm: TDataForm;

2-8 Guide du développeur


Utilisation du modèle objet

AForm est de type TForm et DataForm est de type TDataForm. Comme TDataForm
est un descendant de TForm, l’instruction d’affectation suivante est légale :
AForm := DataForm;
Supposez que vous remplissiez le gestionnaire d’événement de l’événement
OnClick d’un bouton. Quand le bouton est choisi, le gestionnaire d’événement de
l’événement OnClick est appelé. Chaque gestionnaire d’événement a un
paramètre Sender de type TObject :
procedure TForm1.Button1Click(Sender: TObject);
begin
ƒ
end;
Comme Sender est de type TObject, tout objet peut être affecté à Sender. La valeur
de Sender est toujours le contrôle ou le composant qui répond à l’événement.
Vous pouvez tester Sender pour déterminer le type du composant ou du contrôle
qui a appelé le gestionnaire d’événement en utilisant le mot réservé is. Par
exemple :
if Sender is TEdit then
FaireQuelquechose
else
FaireAutreChose;

Création, instanciation et destruction d’objets


La plupart des objets que vous utilisez dans Delphi sont des composants qui,
comme les boutons ou les zones de saisie, apparaissent à la conception et à
l’exécution. Certains, comme les boîtes de dialogue standard, apparaissent
uniquement à l’exécution. D’autres, comme les composants timer ou source de
données, n’ont pas de représentation visuelle dans l’application à l’exécution.
Vous pouvez être amené à créer vos propres objets. Vous pouvez, par exemple,
créer un objet TEmploye contenant les champs Nom, Fonction et SalaireHoraire.
Vous pouvez ensuite lui ajouter la méthode CalculPaie qui utilise les données du
champ SalaireHoraire pour calculer le montant de la paye. La déclaration du type
TEmployee peut prendre la forme suivante :
type
TEmploye = class(TObject)
private
FNom: string;
FFonction: string;
FSalaireHoraire: Real;
public
property Nom: string read FNom write FNom;
property Fonction: string read FFonction write FFonction;
property SalaireHoraire: Real read FSalaireHoraire write FSalaireHoraire;
function CalculPaie: Real;
end;

Utilisation du Pascal Objet avec la VCL 2-9


Utilisation des composants

Outre les champs, propriétés et méthodes que vous avez défini, TEmployee hérite
de toutes les méthodes de TObject. Vous pouvez placer une déclaration de type
comme celle-ci dans la partie interface ou dans la partie implementation d’une
unité et créer des instances de la nouvelle classe en appelant la méthode Create
que TEmploye hérite de TObject :
var
Employe: TEmploye;
begin
Employe := TEmploye.Create;
end;
La méthode Create est appelée un constructeur. Elle alloue la mémoire pour un
objet nouvellement instancié et renvoie une référence sur l’objet.
Les composants d’une fiche sont créés et détruits automatiquement par Delphi.
Mais si vous écrivez votre propre code pour instancier des objets, vous êtes
responsable de leur libération. Chaque objet hérite de TObject une méthode
Destroy (appelée un destructeur). Néanmoins, pour détruire un objet, vous devez
toujours appeler la méthode Free (également héritée de TObject) car Free vérifie
que l’objet existe encore avant d’appeler Destroy. Par exemple :
Employe.Free
détruit l’objet Employe et libère sa mémoire.

Composants et appartenance
Delphi dispose d’un mécanisme intégré de gestion de la mémoire qui permet à
un composant d’être responsable de la libération d’un autre composant. On dit
que le premier est propriétaire du second. La mémoire d’un composant
appartenant à un autre est automatiquement libérée quand la mémoire du
propriétaire est libérée. Le propriétaire d’un composant (valeur de sa propriété
Owner) est déterminé par un paramètre transmis au constructeur lors de la
création du composant. Par défaut, une fiche possède tous les composants placés
dedans et elle-même appartient à l’application. Ainsi, quand l’application est
arrêtée, la mémoire de toutes les fiches et de leurs composants est libérée.
Cette notion de propriétaire ne s’applique qu’à TComponent et à ses descendants.
Si vous créez un objet TStringList ou TCollection (même s’il est associé à une
fiche), c’est à vous de libérer l’objet.
Remarque Ne confondez pas le propriétaire d’un composant avec son parent. Voir “Propriétés
du parent” à la page 2-12.

Utilisation des composants


Tous les composants partagent les caractéristiques héritées de TComponent. En
plaçant des composants dans des fiches, vous concevez l’interface et les
fonctionnalités d’une application. Les composants standard proposés par Delphi
sont suffisants pour concevoir la plupart des applications, mais vous pouvez
étendre la VCL en créant des composants personnalisés. Pour davantage
d’informations sur la création de composants personnalisés, voir chapitre 31,
“Présentation générale de la création d’un composant.”

2-10 Guide du développeur


Utilisation des composants

Composants standard de Delphi


La palette des composants contient une sélection de composants qui gèrent une
grande variété d’opérations de programmation. Vous pouvez ajouter, retirer ou
réorganiser les composants de la palette et vous pouvez créer des modèles de
composants et des cadres qui regroupent plusieurs composants.
Les composants ayant des fonctions similaires sont regroupés en pages dans la
palette des composants. Les pages apparaissant dans la configuration par défaut
dépendent de la version de Delphi que vous utilisez. Le tableau 2.1 énumère les
pages définies par défaut et le type de composants qu’elles contiennent.

Tableau 2.1 Les pages de la palette de composants


Nom de page Contenu
Standard Contrôles Windows standard, menus.
Supplément Contrôles personnalisés
Win32 Contrôles communs Windows 95/NT 4.0.
Système Composants et contrôles permettant un accès au niveau du système, y
compris les timers, le système de fichier, le multimédia et le DDE.
Internet Composants proposant des protocoles de communication Internet et les
applications Web
AccèsBD Composants non visuels qui accèdent aux bases de données, aux requêtes
et aux états.
ContrôleBD Composants visuels orientés données.
Decision Cube Des contrôles permettant de faire des statistiques sur les informations
contenues dans des bases de données et de les visualiser sous différents
angles de vue.
QReport Composants Quick Report utilisés pour créer des états incorporés.
Dialogues Boîtes de dialogue standard Windows.
Win 3.1 Composants proposés pour la compatibilité avec les projets Delphi 1.0.
Exemples Composants personnalisés exemple.
ActiveX Contrôles ActiveX exemple.
Midas Composants MIDAS utilisés pour créer des applications de base de
données multiniveaux.

L’aide en ligne propose des informations sur les composants de la palette par
défaut. Cependant, les composants des pages ActiveX et Exemples sont proposés
uniquement à titre d’exemple et ne sont pas documentés.

Propriétés communes à tous les composants


Tous les composants visuels (descendants de TControl) partagent certaines
propriétés, entre autres :
• Propriétés de position et de taille
• Propriétés d’affichage
• Propriétés du parent
• Propriétés de déplacement

Utilisation du Pascal Objet avec la VCL 2-11


Utilisation des composants

• Propriétés de glisser-déplacer
• Propriétés de glisser-empiler
Ces propriétés sont héritées de TControl mais elles ne sont publiées (elles
apparaissent dans l’inspecteur d’objet) que pour les composants où elles sont
applicables. Ainsi, TImage ne publie pas la propriété Color car sa couleur est
déterminée par l’image qu’il affiche.

Propriétés de position et de taille


Quatre propriétés définissent la position et la taille d’un contrôle dans une fiche :
• Height spécifie la taille verticale.
• Width spécifie la taille horizontale.
• Top indique la position du bord supérieur.
• Left indique la position du bord gauche.
Ces propriétés ne sont pas accessibles dans les composants non visuels, mais
Delphi se souvient d’où vous avez placé les icônes de ces composants dans une
fiche. La plupart du temps, vous définirez ou modifierez ces propriétés en
manipulant l’image du contrôle dans la fiche ou en utilisant la palette
Alignement. Vous pouvez également les modifier à l’exécution.

Propriétés d’affichage
Quatre propriétés définissent l’aspect général d’un contrôle :
• BorderStyle spécifie si le contrôle a une bordure.
• Color spécifie la couleur de fond d’un contrôle.
• Ctrl3D spécifie si le contrôle a un aspect 3D ou un bord plat.
• Font modifie la couleur, le nom, le style ou la taille du texte.

Propriétés du parent
Pour conserver une présentation homogène dans une application, vous pouvez
donner à un contrôle le même aspect que son conteneur, appelé son parent, en
initialisant les propriétés parent... à True. Si, par exemple, vous placez un bouton
dans une fiche et initialisez la propriété ParentFont du bouton à True, les
modifications de la propriété Font de la fiche sont automatiquement répercutées
sur le bouton (et aux autres enfants de la fiche). Si, ultérieurement, vous
modifiez la propriété Font du bouton, la valeur spécifiée est prise en compte et la
propriété ParentFont passe automatiquement à False.
Remarque Ne confondez pas le parent d’un composant avec son propriétaire. Voir
“Composants et appartenance” à la page 2-10.

Propriétés de déplacement
Plusieurs propriétés déterminent comment les utilisateurs peuvent se déplacer
entre les divers contrôles d’une fiche :
• Caption contient la chaîne de texte qui intitule un composant. Pour souligner un
caractère d’une chaîne, faites-le précéder d’un caractère &. Ce type de caractère
est appelé un caractère accélérateur. L’utilisateur peut alors accéder au contrôle
ou à l’élément de menu en appuyant sur Alt et sur le caractère souligné.

2-12 Guide du développeur


Utilisation des composants

• TabOrder indique la position du contrôle dans l’ordre de tabulation du parent :


l’ordre dans lequel les contrôles reçoivent la focalisation quand l’utilisateur
appuie sur la touche Tab. Initialement, l’ordre de tabulation correspond à
l’ordre d’ajout des composants dans la fiche. Vous pouvez le modifier en
changeant TabOrder. TabOrder n’a de sens que si TabStop a la valeur True.
• TabStop détermine si l’utilisateur peut tabuler sur un contrôle. Si TabStop a la
valeur True, le contrôle est dans l’ordre de tabulation.

Propriétés de glisser-déplacer
Deux propriétés des composants affectent le comportement du glisser-déplacer :
• DragMode détermine la manière dont un glisser commence. Par défaut,
DragMode a la valeur dmManual et vous devez appeler la méthode BeginDrag
pour commencer un glisser. Si DragMode a la valeur dmAutomatic, le glisser
commence dès que le bouton de la souris est enfoncé.
• DragCursor détermine la forme du pointeur de la souris quand il se trouve au-
dessus d’un composant qui accepte un objet en train de glisser.

Propriétés de glisser-empiler
Les propriétés suivantes des composants contrôlent le comportement de glisser-
empiler :
• DockSite
• DragKind
• DragMode
• FloatingDockSiteClass
• AutoSize
Pour davantage d’informations, voir “Implémentation du glisser-empiler dans les
contrôles” à la page 6-4.

Contrôles texte
Dans de nombreuses applications, l’utilisateur doit saisir ou consulter du texte.
Le type de contrôle à employer pour contenir les informations dépend de la
taille et du format des informations.

Utilisez ce
composant : Quand vous voulez :
Edit Modifier une seule ligne de texte.
Memo Modifier plusieurs lignes de texte.
MaskEdit Utiliser un format particulier, par exemple celui d’un code postal ou
d’un numéro de téléphone.
RichEdit Modifier une quantité illimitée de texte ou utiliser du texte mis en
forme.

Utilisation du Pascal Objet avec la VCL 2-13


Utilisation des composants

Propriétés communes à tous les contrôles texte


Tous les contrôles texte ont en commun les propriétés suivantes :
• Text détermine le texte qui apparaît dans la boîte de saisie ou le contrôle
mémo.
• CharCase convertit les caractères saisis en minuscules, en majuscules ou en une
combinaison des deux.
• ReadOnly spécifie si l’utilisateur peut faire des modifications.
• MaxLength limite le nombre de caractères du contrôle
• PasswordChar masque le texte en affichant un seul caractère (généralement un
astérisque).
• HideSelection spécifie si le texte sélectionné reste mis en évidence quand le
contrôle ne détient pas la focalisation.

Propriétés communes aux contrôles mémo et éditeur de texte formaté


Les contrôles mémo et éditeur de texte formaté, qui gèrent plusieurs lignes de
texte, ont plusieurs propriétés en commun :
• Alignment spécifie comment le texte est aligné (gauche, droite ou centré) à
l’intérieur du composant.
• La propriété Text contient le texte du contrôle. Votre application peut
déterminer si le texte a été modifié en examinant la propriété Modified.
• Lines contient le texte sous la forme d’une liste de chaînes.
• OEMConvert détermine si le texte du contrôle est converti en caractères OEM.
Cela s’avère utile pour valider les noms de fichiers.
• WordWrap détermine si le texte revient à la ligne après la marge droite.
• WantReturns détermine si l’utilisateur peut insérer des passages à la ligne dans
le texte.
• WantTabs détermine si l’utilisateur peut insérer des tabulations dans le texte.
• AutoSelect détermine si le texte est automatiquement sélectionné (mis en
évidence) quand le contrôle devient actif.
• SelText contient la partie du texte actuellement sélectionnée (mise en évidence).
• SelStart et SelLength indiquent la position des premier et dernier caractères de
la partie sélectionnée du texte.
A l’exécution, vous pouvez sélectionner tout le texte d’un mémo en utilisant la
méthode SelectAll.

Contrôles éditeurs de texte formaté


Le composant éditeur de texte formaté est un composant mémo qui gère le texte
mis en forme, l’impression, la recherche et le glisser-déplacer du texte. Il vous
permet de spécifier les propriétés de police, d’alignement, de tabulation,
d’indentation et de numérotation.

2-14 Guide du développeur


Utilisation des composants

Contrôles de saisies spécialisées


Les composants suivants proposent d’autres méthodes pour recevoir des saisies.

Utilisez ce
composant : Quand l’utilisateur doit :
ScrollBar sélectionner des valeurs dans un intervalle continu.
TrackBar sélectionner des valeurs dans un intervalle continu (visuellement plus
parlant qu’une barre de défilement).
UpDown sélectionner une valeur à l’aide d’un incrémenteur associé à un
composant de saisie
HotKey entrer des séquences clavier Ctrl/Maj/Alt.

Barres de défilement
Le composant barre de défilement est une barre de défilement Windows, utilisée
pour faire défiler le contenu d’une fenêtre, d’une fiche ou d’un autre contrôle. Le
code écrit dans le gestionnaire d’événement OnScroll détermine comment la
fenêtre, la fiche ou le contrôle se comporte quand l’utilisateur fait défiler la barre
de défilement.
Le composant barre de défilement est rarement utilisé car la plupart des
composants visuels disposent de leurs propres barres de défilement sans
nécessiter de programmation. Ainsi, TForm propose les propriétés VertScrollBar et
HorzScrollBar qui configurent automatiquement des barres de défilement pour la
fiche. Pour créer une région défilante dans une fiche, utilisez TScrollBox.

Contrôles barre graduée


Le composant barre graduée peut initialiser ou modifier des valeurs entières
dans un intervalle continu, par exemple pour ajuster la valeur de propriétés
comme le volume du son ou la luminosité. L’utilisateur déplace la glissière en la
faisant glisser à une position donnée ou en cliquant dans la barre.
• Utilisez les propriétés Max et Min pour définir les bornes supérieure et
inférieure de l’intervalle de la barre de défilement.
• Utilisez SelEnd et SelStart pour mettre en évidence un intervalle sélectionné.
Voir figure 2.3. La propriété Orientation détermine si la barre graduée est
verticale ou horizontale.
• Par défaut, une barre graduée dispose d’une ligne de graduations en bas.
Utilisez la propriété TickMarks pour modifier leur emplacement. Pour contrôler
l’espacement des graduations, utilisez la propriété TickStyle et la méthode
SetTicks.
Figure 2.3 Trois vues du composant barre graduée

• Position définit la position par défaut dans la barre graduée et indique à


l’exécution la valeur sélectionnée par l’utilisateur.

Utilisation du Pascal Objet avec la VCL 2-15


Utilisation des composants

• Par défaut, l’utilisateur peut se déplacer d’une graduation vers le haut ou vers
le bas en utilisant les touches de déplacement correspondantes. Affectez
LineSize pour changer cet incrément.
• Affectez PageSize pour déterminer le nombre de graduations du déplacement
quand l’utilisateur appuie sur les touches Pg. Haut et Pg. Bas.

Contrôles flèches haut-bas


Un contrôle flèches haut-bas est constitué d’une paire de boutons fléchés qui
permettent à l’utilisateur de modifier une valeur entière d’un incrément fixe. La
valeur en cours est donnée par la propriété Position ; l’incrément, qui vaut 1 par
défaut, est spécifié par la propriété Increment. Utilisez la propriété Associate pour
attacher un autre composant (par exemple, un contrôle de saisie) au contrôle
flèches haut-bas.

Contrôles touche d’accès rapide


Utilisez le composant touche d’accès rapide pour affecter une séquence de
touches qui transfère la focalisation à un composant. La propriété HotKey
contient la combinaison de touches en cours et la propriété Modifiers détermine
les touches disponibles pour HotKey.

Contrôle séparateur
Un séparateur placé entre deux contrôles alignés permet aux utilisateurs de
redimensionner les contrôles. Utilisé avec des composants comme les volets ou
les boîtes de groupe, les séparateurs vous permettent de décomposer une fiche
en plusieurs volets contenant chacun plusieurs contrôles.
Après avoir placé un volet ou un autre contrôle dans une fiche, ajoutez un
séparateur ayant le même alignement que le contrôle. Le dernier contrôle doit
être aligné sur le client afin qu’il remplisse tout l’espace restant quand les autres
sont redimensionnés. Vous pouvez, par exemple, placer un volet sur le bord
gauche d’une fiche et initialiser sa propriété Alignment à alLeft puis placer un
séparateur (ayant également l’alignement alLeft) à droite du volet, et enfin placer
un autre volet (avec l’alignement alLeft ou alClient) à droite du séparateur.
Initialisez MinSize afin se spécifier la taille minimum que le séparateur doit
laisser quand il redimensionne le contrôle adjacent. Initialisez Beveled à True pour
donner au séparateur un aspect 3D.

Contrôles bouton et similaires


En dehors des menus, les boutons constituent le moyen le plus simple de
déclencher une commande dans une application. Delphi propose plusieurs
contrôles de type bouton :

Utilisez ce composant : Pour :


Button Présenter des choix de commandes avec du texte dans des
boutons.
BitBtn Présenter des choix de commandes dans des boutons
contenant du texte et des glyphes.

2-16 Guide du développeur


Utilisation des composants

Utilisez ce composant : Pour :


SpeedButton Créer des groupes de boutons dans des barres d’outils.
CheckBox Présenter des options de type Oui/Non.
RadioButton Présenter un ensemble de choix mutuellement exclusifs.
ToolBar Disposer des boutons et d’autres contrôles en ligne et ajuster
automatiquement leur taille et leur position.
CoolBar Afficher une collection de contrôles fenêtrés dans des bandes
déplaçables et redimensionnables.

Contrôles bouton
Les utilisateurs cliquent sur les contrôles bouton pour initier des actions. En
double-cliquant sur un bouton à la conception, vous affichez le gestionnaire
d’événement OnClick du bouton dans l’éditeur de code.
• Affectez la valeur True à la propriété Cancel pour que le bouton déclenche son
événement OnClick quand l’utilisateur appuie sur Echap.
• Affectez la valeur True à la propriété Default pour que la touche Entrée
déclenche l’événement OnClick du bouton.

Boutons bitmap
Un bouton bitmap (BitBtn) est un contrôle bouton qui contient une image bitmap.
• Pour attribuer un bitmap personnalisé à votre bouton, affectez la propriété
Glyph.
• Utilisez la propriété Kind pour configurer automatiquement un bouton avec
un glyphe et un comportement par défaut.
• Par défaut, le glyphe est à gauche du texte. Pour le déplacer, utilisez la
propriété Layout.
• Le glyphe et le texte sont automatiquement centrés dans le bouton. Pour
changer leur position, utilisez la propriété Margin. Margin détermine le
nombre de pixels entre le bord de l’image et le bord du bouton.
• Par défaut, l’image et le texte sont séparés par 4 pixels. Utilisez Spacing pour
augmenter ou réduire cette distance.
• Les boutons bitmap peuvent avoir 3 états : haut, bas et enfoncé. Affectez la valeur
3 à la propriété NumGlyphs pour attribuer un bitmap différent à chaque état.

Turboboutons
Les turboboutons, qui affichent généralement une image, peuvent fonctionner en
groupe. Ils sont souvent utilisés avec des volets pour créer des barres d’outils.
• Pour faire fonctionner des turboboutons en groupe, affectez à la propriété
GroupIndex de tous les boutons la même valeur non-nulle.
• Par défaut, des turboboutons apparaissent à l’état haut (non sélectionné). Pour
afficher un turbobouton à l’état sélectionné, affectez la valeur True à sa
propriété Down.

Utilisation du Pascal Objet avec la VCL 2-17


Utilisation des composants

• Si AllowAllUp a la valeur True, tous les turboboutons d’un groupe peuvent


être non sélectionnés. Affectez la valeur False à AllowAllUp pour qu’un groupe
de boutons se comporte comme un groupe de boutons radio.

Cases à cocher
Une case à cocher est une bascule qui propose à l’utilisateur deux ou trois choix.
• Affectez True à Checked pour que la case soit cochée par défaut.
• Affectez True à AllowGrayed pour que la case à cocher puisse prendre trois
états : cochée, non-cochée et grisée.
• La propriété State indique si la case est cochée (cbChecked), non-cochée
(cbUnchecked) ou grisée (cbGrayed).

Boutons radio
Les boutons radio proposent un ensemble de choix mutuellement exclusifs. Vous
pouvez utiliser des composants bouton radio individuels ou utiliser le composant
groupe de boutons radio qui regroupe automatiquement des boutons radio. Pour
davantage d’informations, voir “Regroupement de composants” à la page 2-21.

Barres d’outils
Les barres d’outils permettent aisément d’organiser et de gérer des contrôles
visuels. Vous pouvez créer une barre d’outils à partir d’un composant volet et de
turboboutons, ou utiliser le composant ToolBar puis choisir Nouveau bouton dans
son menu contextuel pour chaque bouton à ajouter. L’utilisation de cette dernière
méthode présente plusieurs avantages. Les boutons d’une barre d’outils ont
automatiquement des dimensions et un espacement homogènes, les autres
contrôles conservent leur position et hauteur relatives ; les contrôles peuvent
automatiquement passer à la ligne s’il n’y a pas assez de place horizontalement.
Le composant ToolBar propose également des options comme la transparence, les
bordures en relief et les espaces et des séparations pour regrouper des contrôles.

Barres multiples
Une barre multiple contient des contrôles enfant qui peuvent être déplacés ou
redimensionnés de manière indépendante. Chaque contrôle se trouve dans une
bande indépendante. L’utilisateur positionne les contrôles en utilisant la poignée
de redimensionnement à gauche de chaque bande.
La barre multiple exige, à la conception et à l’exécution, une version 4.70, ou
ultérieure, de COMCTL32.DLL (qui se trouve généralement dans le répertoire
WINDOWS\SYSTEM ou WINDOWS\SYSTEM32).
• La propriété Bands contient une collection d’objets TCoolBand. A la conception,
vous pouvez ajouter, retirer ou modifier les bandes à l’aide de l’éditeur de
bandes. Pour l’ouvrir, sélectionnez la propriété Bands dans l’inspecteur d’objet
puis double-cliquez dans la colonne des valeurs à droite ou cliquez sur le
bouton points de suspension (...). Vous pouvez également ajouter des bandes
simplement en ajoutant de nouveaux contrôles fenêtrés de la palette.

2-18 Guide du développeur


Utilisation des composants

• La propriété FixedOrder détermine si les utilisateurs peuvent réorganiser les


bandes.
• La propriété FixedSize détermine si les bandes ont une hauteur uniforme.

Gestion de listes
Les listes proposent à l’utilisateur une collection d’éléments dans laquelle il peut
choisir. Plusieurs composants affichent des listes :

Utilisez ce
composant : Pour afficher :
ListBox Une liste de chaînes de texte.
CheckListBox Une liste avec une case à cocher devant chaque élément.
ComboBox Une boîte de saisie avec une liste surgissante déroulante.
TreeView Une liste hiérarchique.
ListView Une liste d’éléments (déplaçables) avec éventuellement des icônes,
des en-têtes et des colonnes.
DateTimePicker Une boîte liste permettant de saisir des dates ou des heures.
MonthCalendar Un calendrier permettant de sélectionner des dates.

Utilisez les composants non-visuels TStringList et TImageList pour gérer des


ensembles de chaînes ou d’images. Pour davantage d’informations sur les listes
de chaînes, voir “Utilisation des listes de chaînes” à la page 2-32.

Boîtes liste et boîtes check-list


Les boîtes liste et les boîtes check-list affichent une liste dans laquelle l’utilisateur
peut sélectionner des éléments.
• Items utilise un objet TStringList pour remplir le contrôle avec des valeurs.
• ItemIndex indique l’élément sélectionné dans la liste.
• MultiSelect spécifie si l’utilisateur peut sélectionner plusieurs éléments à la fois.
• Sorted détermine si la liste est triée alphabétiquement.
• Columns spécifie le nombre de colonnes dans le contrôle liste.
• IntegralHeight spécifie si la boîte liste n’affiche que des entrées affichées en
entier verticalement.
• ItemHeight spécifie la hauteur, exprimée en pixels, d’un élément de la liste. La
propriété Style peut neutraliser l’effet de ItemHeight.
• La propriété Style détermine comment une boîte liste affiche ses éléments. Par
défaut, les éléments sont affichés sous la forme d’une chaîne. En modifiant la
valeur de Style, vous pouvez créer des boîtes liste dessinées par le propriétaire,
dans ce cas les éléments peuvent être graphiques et de hauteur fixe ou de
hauteur variable. Pour plus d’informations sur les contrôles dessinés par le
propriétaire, voir “Ajout de graphiques à des contrôles” à la page 6-13.

Utilisation du Pascal Objet avec la VCL 2-19


Utilisation des composants

Boîtes à options
Une boîte à options combine une boîte de saisie et une liste déroulante. Quand
les utilisateurs saisissent des données, en entrant du texte dans la boîte de saisie
ou en sélectionnant un élément de la liste, la valeur de la propriété Text change.
Utilisez la propriété Style pour spécifier le type de boîte à options que vous
souhaitez utiliser :
• Utilisez csDropdown si vous voulez une boîte de saisie avec une liste
déroulante. Utilisez csDropDownList pour que la boîte de saisie soit en lecture
seule (ce qui oblige les utilisateurs à sélectionner dans la liste). Initialisez la
propriété DropDownCount pour changer le nombre d’éléments affichés dans la
liste.
• Utilisez csSimple pour créer une boîte à options avec une liste fixe qui reste
toujours ouverte. Prenez soin de redimensionner la boîte à options pour que
les éléments de la liste soient affichés.
• Utilisez csOwnerDrawFixed ou csOwnerDrawVariable pour créer des boîtes à
options dessinées par le propriétaire qui affichent des éléments graphiques ou de
hauteur variable. Pour plus d’informations sur les contrôles dessinés par le
propriétaire, voir “Ajout de graphiques à des contrôles” à la page 6-13.

Vues arborescentes
Une vue arborescente affiche des éléments dans une table des matières indentée.
Le contrôle propose des boutons qui permettent de développer ou de réduire les
noeuds. Vous pouvez inclure des icônes en plus du libellé des éléments et
afficher différentes icônes pour indiquer si un noeud est développé ou réduit.
Vous pouvez également inclure des éléments graphiques, par exemple des cases
à cocher, afin de refléter des informations sur l’état des éléments.
• Indent spécifie le nombre de pixels qui séparent horizontalement les éléments
de leur parents.
• ShowButtons active l’affichage des boutons '+' et '–' pour indiquer si un
élément peut être développé.
• ShowLines active l’affichage de lignes de connexion qui montrent les relations
hiérarchiques.
• ShowRoot détermine si des lignes connectent les éléments racine.

Vues liste
Les vues liste affichent des listes dans divers formats. Utilisez la propriété
ViewStyle pour choisir le type de liste utilisé :
• vsIcon et vsSmallIcon affichent chaque élément sous la forme d’une icône avec
un libellé. Les utilisateurs peuvent faire glisser les éléments dans la fenêtre de
la vue liste.
• vsList affiche les éléments comme icônes libellées qui ne peuvent pas être
déplacées.

2-20 Guide du développeur


Utilisation des composants

• vsReport affichent les éléments à raison d’un par ligne avec des informations
organisées en colonnes. La colonne de gauche contient une petite icône et un
libellé et les autres colonnes contiennent des sous-éléments spécifiés par
l’application. Utilisez la propriété ShowColumnHeaders afin d’afficher des en-
têtes de colonne.

Sélecteurs Date/Heure et calendriers mensuels


Le composant sélecteur date/heure affiche une boîte liste permettant de saisir
des dates ou des heures. Le composant calendrier mensuel propose un calendrier
permettant de saisir des dates ou des plages de dates. Pour utiliser ces
composants, que ce soit à la conception ou à l’exécution, vous devez disposer de
la dernière version de COMCTL32.DLL (placée normalement dans le répertoire
WINDOWS\SYSTEM ou WINDOWS\SYSTEM32).

Regroupement de composants
Une interface utilisateur graphique est plus facile à utiliser quand des contrôles
et les contrôles associés sont présentés dans des groupes. Delphi propose
plusieurs composants permettant de regrouper des composants :

Utilisez ce
composant : Pour :
GroupBox Une boîte groupe standard avec un titre.
RadioGroup Un groupe simple de boutons radio.
Panel Un groupe de contrôles plus flexible visuellement.
ScrollBox Une zone défilante contenant des contrôles.
TabControl Un ensemble d’onglets (du type classeur) mutuellement exclusifs.
PageControl Un ensemble d’onglets (du type classeur) mutuellement exclusifs avec
les pages correspondantes, chacune pouvant contenir d’autres
contrôles.
HeaderControl Des en-têtes de colonne redimensionnables.

Boîtes groupes et groupes de boutons radio


Une boîte groupe est un composant standard Windows qui associe des contrôles
d’une fiche. Les contrôles les plus fréquemment regroupés sont les boutons
radio. Après avoir placé une boîte groupe dans une fiche, sélectionnez les
composants dans la palette de composants et placez-les dans la boîte groupe. La
propriété Caption contient le texte qui sert à libeller la boîte groupe à l’exécution.
Le composant groupe de boutons radio simplifie le regroupement de boutons
radio et gère leur fonctionnement en commun. Pour ajouter des boutons radio à
un groupe, modifiez la propriété Items dans l’inspecteur d’objet ; chaque chaîne
de Items constitue un bouton radio qui apparaît dans le groupe en utilisant la
chaîne spécifiée comme libellé. La valeur de la propriété ItemIndex détermine le
bouton radio sélectionné. Affichez les boutons radio sur une ou plusieurs
colonnes en définissant la valeur de la propriété Columns. Pour espacer les
boutons, redimensionnez le composant groupe de boutons radio.

Utilisation du Pascal Objet avec la VCL 2-21


Utilisation des composants

Volets
Le composant volet constitue un conteneur générique pour d’autres contrôles. Il
est possible d’aligner des volets dans la fiche pour conserver la même position
relative quand la fiche est redimensionnée. La propriété BorderWidth détermine la
largeur, en pixels, de la bordure entourant un volet.

Boîtes de défilement
Les boîtes de défilement permettent de créer des zones défilantes à l’intérieur d’une
fiche. Souvent, les applications ont besoin d’afficher plus d’informations qu’il ne
peut apparaître dans une zone particulière. Certains contrôles, comme les boîtes
liste, les mémos ou les fiches mêmes, peuvent automatiquement faire défiler leur
contenu. Les boîtes de défilement vous offrent davantage de souplesse en vous
permettant de définir arbitrairement une zone défilante dans une fiche.
Comme les volets et les boîtes groupe, les boîtes de défilement contiennent
d’autres contrôles. Mais, normalement une boîte de défilement est invisible. Si les
contrôles qu’elle contient ne peuvent rentrer dans sa partie visible, la boîte de
défilement affiche automatiquement des barres de défilement.

Contrôles onglets
Le composant contrôle onglets a l’aspect des séparateurs d’un classeur. Vous
pouvez créer des onglets en modifiant la propriété Tabs à l’aide de l’inspecteur
d’objet ; chaque chaîne de Tabs représente un onglet. Le contrôle onglets est un
simple volet avec un seul ensemble de composants dedans. Pour changer l’aspect
du contrôle quand les onglets sont sélectionnés, écrivez un gestionnaire
d’événement OnChange. Pour créer une boîte de dialogue multipage, utilisez
plutôt un contrôle pages.

Contrôles pages
Le composant contrôle pages est un ensemble de pages utilisé pour constituer
une boîte de dialogue multipage. Pour créer une nouvelle page dans le contrôle
page, cliquez avec le bouton droit de la souris sur le contrôle pages et choisissez
Nouvelle page.

Contrôles en-tête
Un contrôle en-tête est un ensemble d’en-têtes de colonnes que l’utilisateur peut
sélectionner ou redimensionner à l’exécution. Modifiez la propriété Sections du
contrôle pour ajouter ou modifier les en-têtes.

Rétroaction visuelle
Il existe plusieurs moyens de donner à l’utilisateur des informations sur l’état
d’une application. Par exemple, certains composants, dont TForm, disposent de la
propriété Caption qui peut être définie à l’exécution. Vous pouvez également
créer des boîtes de dialogue pour afficher des messages. De plus, les composants

2-22 Guide du développeur


Utilisation des composants

suivants sont particulièrement utiles pour fournir des indications visuelles à


l’exécution.

Utilisez ce composant
ou cette propriété : Pour :
Label et StaticText Afficher du texte non modifiable.
StatusBar Afficher une zone d’état (généralement en bas d’une fenêtre).
ProgressBar Afficher le pourcentage effectué d’une tâche donnée.
Hint et ShowHint Activer les conseils d’aide (appelés aussi bulles d’aide).
HelpContext et HelpFile Effectuer la liaison avec le système d’aide en ligne.

Libellés et composants texte statique


Les libellés affichent du texte, ils sont généralement placés à côté d’autres
composants. Le composant libellé standard label, TLabel, est un contrôle non-
fenêtré qui ne peut donc pas recevoir la focalisation ; si vous avez besoin d’un
libellé disposant d’un handle de fenêtre, utilisez TStaticText à la place. Les
propriétés importantes d’un libellé sont :
• Caption contient la chaîne de texte du libellé.
• FocusControl relie le contrôle libellé à un autre contrôle de la fiche. Si l’intitulé
du libellé comporte une touche accélératrice, le contrôle spécifié dans la
propriété FocusControl obtient la focalisation quand l’utilisateur appuie sur la
touche de raccourci.
• ShowAccelChar détermine si le libellé peut afficher un caractère de raccourci
souligné. Si ShowAccelChar a la valeur True, tout caractère précédé d’un &
apparaît souligné et active une touche de raccourci.
• Transparent détermine si les éléments sur lesquels le libellé est placé (par
exemple des images) sont visibles.

Barres d’état
Même si vous pouvez utiliser un volet pour créer une barre d’état, il est plus
simple d’utiliser le composant barre d’état. Par défaut, la propriété Align d’une
barre d’état a la valeur alBottom, ce qui gère à la fois la position et la taille.
Généralement une barre d’état est divisée en plusieurs zones de texte. Pour créer
des zones, modifiez la propriété Panels avec l’inspecteur d’objet et spécifiez les
propriétés Width, Alignment et Text de chaque volet à l’aide de l’éditeur de
volets. La propriété Text contient le texte affiché dans le volet.

Barres de progression
Quand une application effectue une opération longue, vous pouvez utiliser une
barre de progression pour indiquer le pourcentage réalisé de l’opération. Une
barre de progression affiche une ligne pointillée qui progresse de gauche à droite.
Figure 2.4 Une barre de progression

Utilisation du Pascal Objet avec la VCL 2-23


Utilisation des composants

La propriété Position indique la longueur de la ligne pointillée. Max et Min


déterminent l’étendue des valeurs prises par Position. Pour allonger la ligne,
augmentez Position en appelant la méthode StepBy ou StepIt. La propriété Step
détermine l’incrément utilisé par StepIt.

Propriétés d’aide ou de conseil d’aide


La plupart des contrôles visuels peuvent, à l’exécution, afficher de l’aide
contextuelle ou des conseils d’aide. Les propriétés HelpContext et HelpFile
spécifient un numéro de contexte d’aide le nom du fichier d’aide pour un
contrôle.
La propriété Hint spécifie la chaîne de texte qui apparaît quand l’utilisateur
déplace le pointeur de la souris au-dessus d’un contrôle ou d’un élément de
menu. Pour que ce conseil apparaisse quand l’utilisateur place la souris au-
dessus d’un composant, affectez la valeur True à ShowHint. L’initialisation de
ParentShowHint à True force la propriété ShowHint du contrôle a prendre la
même valeur que celle de son parent.

Grilles
Les grilles affichent des informations disposées en lignes et en colonnes. Si vous
concevez une application de base de données, utilisez les composants TDBGrid et
TDBCtrlGrid décrits dans le chapitre 26, “Utilisation de contrôles de données”.
Sinon, utilisez une grille de dessin ou une grille de chaînes standard.

Grilles de dessin
Une grille de dessin (TDrawGrid) affiche des données quelconques dans un
format tabulaire. Ecrivez un gestionnaire d’événement OnDrawCell pour remplir
les cellules de la grille.
• La méthode CellRect renvoie les coordonnées écran de la cellule spécifiée alors
que la méthode MouseToCell renvoie la colonne et la ligne de la cellule se
trouvant aux coordonnées écran spécifiées. La propriété Selection indique les
limites de la sélection de cellules en cours.
• La propriété TopRow détermine la ligne qui apparaît en haut de la grille. La
propriété LeftCol détermine la première colonne visible sur la gauche de la
grille. VisibleColCount et VisibleRowCount indiquent, respectivement, le nombre
de colonnes et de lignes visibles dans la grille.
• Vous pouvez modifier la largeur et la hauteur d’une colonne ou d’une ligne
en utilisant les propriétés ColWidths et RowHeights. Définissez l’épaisseur des
lignes du quadrillage de la grille avec la propriété GridLineWidth. Ajoutez des
barres de défilement à la grille en utilisant la propriété ScrollBars.
• Vous pouvez spécifier les colonnes ou les lignes fixes (qui ne défilent pas) à
l’aide des propriétés FixedCols et FixedRows. Attribuez une couleur aux
colonnes et aux lignes fixes en utilisant la propriété FixedColor.
• Les propriétés Options, DefaultColWidth et DefaultRowHeight affectent également
l’aspect et le comportement de la grille.

2-24 Guide du développeur


Utilisation des composants

Grilles de chaînes
Le composant grille de chaînes est un descendant de TDrawGrid spécialisé afin
de simplifier l’affichage de chaînes. La propriété Cells énumère les chaînes pour
chaque cellule de la grille ; la propriété Objects énumère les objets associés à
chaque chaîne. Il est possible d’accéder à toutes les chaînes et objets associés
d’une colonne ou d’une ligne donnée en utilisant les propriétés Cols et Rows.

Affichage graphique
Les composants suivants facilitent l’incorporation d’éléments graphiques dans
une application.

Utilisez ce
composant : Pour afficher :
Image des fichiers graphiques.
Shape des formes géométriques.
Bevel des lignes et des cadres en 3D.
PaintBox des graphiques dessinés par l’application à l’exécution.
Animate des fichiers AVI.

Images
Le composant image affiche une image graphique : bitmap, icône ou métafichier.
La propriété Picture spécifie l’image à afficher. Utilisez les propriétés Center,
AutoSize, Stretch et Transparent pour spécifier les options d’affichage.

Formes
Le composant forme affiche une forme géométrique. C’est un contrôle non
fenêtré qui ne peut donc pas recevoir la focalisation. La propriété Shape spécifie
la forme du contrôle. Pour modifier la couleur de la forme ou lui ajouter un
motif, utilisez la propriété Brush qui contient un objet TBrush. Les propriétés
Color et Style de TBrush contrôlent la manière dont la forme est dessinée.

Biseaux
Le composant biseau est une ligne qui peut apparaître en relief ou en creux.
Certains composants comme TPanel disposent de propriétés intégrées pour créer
des contours biseautés. Quand ces propriétés ne sont pas disponibles, utilisez un
composant TBevel pour créer des contours, des boîtes ou des cadres biseautés.

Boîtes à peindre
Le composant boîte à peindre permet à une application de dessiner dans une
fiche. Ecrivez un gestionnaire d’événement OnPaint pour restituer directement
l’image dans le canevas (Canvas) de la boîte à peindre. Il n’est pas possible de
dessiner hors des limites d’une boîte à peindre. Pour davantage d’informations,
voir “Présentation de la programmation relative aux graphiques” à la page 7-1.

Utilisation du Pascal Objet avec la VCL 2-25


Utilisation des composants

Contrôles animation
Le composant animation est une fenêtre qui affiche silencieusement une séquence
vidéo AVI. Une séquence AVI est composée d’une série de plans bitmap, comme
un film. Les séquences AVI peuvent être sonorisées, mais les contrôles animation
ne fonctionnent qu’avec les séquences AVI silencieuses. Les fichiers utilisés
doivent être des fichiers AVI non compressés ou des séquences AVI compressées
en utilisant l’algorithme RLE.

Boîtes de dialogue standard Windows


Les composants boîte de dialogue standard de la page Dialogues de la palette
des composants permettent d’utiliser dans vos applications Delphi les boîtes de
dialogue "standard" Windows. Ces boîtes de dialogue donnent à toutes les
applications Windows une interface cohérente pour des opérations standard
comme la recherche, l’ouverture de fichier, le paramétrage de police ou de
couleur et l’impression. Les boîtes de dialogue n’apparaissent à l’exécution que
lorsqu’elles sont activées en appelant leur méthode Execute.

Initialisation des propriétés d’un composant


Les propriétés publiées peuvent être initialisées à la conception avec l’inspecteur
d’objet ou, dans certains cas, avec des éditeurs de propriétés spécifiques.
Pour spécifier des propriétés à l’exécution, il suffit de leur affecter de nouvelles
valeurs dans le code de l’application.
Pour des informations sur les propriétés de chaque composant, consultez l’aide
en ligne de la VCL.

Utilisation de l’inspecteur d’objet


Quand vous sélectionnez un composant d’une fiche, l’inspecteur d’objet affiche ses
propriétés publiées et vous permet (si c’est approprié) de les modifier. Utilisez la
touche Tab pour vous déplacer entre la colonne des valeurs et la colonne des
propriétés. Si le curseur est dans la colonne des propriétés, vous pouvez vous
positionner sur une propriété en tapant les premières lettres de son nom. Pour
les propriétés de type booléen ou énuméré, vous pouvez choisir une valeur dans
une liste déroulante ou parcourir les valeurs en double-cliquant dans la colonne
des valeurs. Si un symbole plus (+) apparaît à côté du nom de la propriété, vous
pouvez faire apparaître une liste de sous-valeurs pour la propriété en cliquant
sur le symbole plus.
Par défaut, les propriétés de la catégorie Héritage ne sont pas affichées. Pour
modifier les filtres d’affichage, cliquez avec le bouton droit de la souris dans
l’inspecteur d’objet et choisissez Voir. Pour davantage d’informations, voir
“catégories de propriétés” dans l’aide en ligne.

2-26 Guide du développeur


Utilisation des composants

Si plusieurs composants sont sélectionnés, l’inspecteur d’objet affiche toutes les


propriétés, sauf Name, communes aux composants sélectionnés. Si la valeur d’une
propriété partagée n’est pas la même pour tous les composants sélectionnés,
l’inspecteur d’objet affiche soit la valeur par défaut, soit la valeur de la propriété
pour le premier composant sélectionné. Quand vous modifiez une propriété
partagée, la modification s’applique à tous les composants sélectionnés.

Utilisation des éditeurs de propriété


Certaines propriétés, comme Font utilisent des éditeurs de propriétés spécifiques.
Quand une telle propriété est sélectionnée dans l’inspecteur d’objet, un bouton
points de suspension (...) apparaît à côté de sa valeur. Pour ouvrir l’éditeur de
propriété, double-cliquez dans la colonne des valeurs ou cliquez sur le bouton
points de suspension. Pour certains composants, il suffit de double-cliquer sur le
composant dans la fiche pour ouvrir un éditeur de propriété.
Les éditeurs de propriété permettent de définir des propriétés complexes à partir
d’une seule boîte de dialogue. Elles valident les saisies et permettent souvent de
prévisualiser les effets d’une affectation.

Initialisation des propriétés à l’exécution


Vous pouvez à l’exécution utiliser votre code source pour affecter une valeur à
toute propriété accessible en écriture. Vous pouvez ainsi, définir de manière
dynamique le libellé d’une fiche :
Form1.Caption := MaChaine;

Appel de méthodes
Une méthode s’appelle comme une procédure ou une fonction ordinaire. Ainsi,
les contrôles visuels disposent de la méthode Repaint qui rafraîchit l’image du
contrôle à l’écran. Vous pouvez appeler la méthode Repaint d’un objet grille de
dessin de la manière suivante :
DrawGrid1.Repaint;
Comme pour les propriétés, c’est la portée d’une méthode qui impose ou pas
l’utilisation de qualificateurs. Par exemple, pour redessiner une fiche depuis le
gestionnaire d’événement de l’un des contrôles enfant de la fiche, il n’est pas
nécessaire de préfixer l’appel de méthode avec le nom de la fiche :
procedure TForm1.Button1Click(Sender: TObject);
begin
Repaint;
end;
Pour davantage d’informations sur la portée, voir “Portée et qualificateurs” à la
page 2-7.

Utilisation du Pascal Objet avec la VCL 2-27


Utilisation des composants

Utilisation des événements et des gestionnaires


d’événements
Dans Delphi, l’essentiel du code que vous écrivez est exécuté, directement ou
indirectement, en réponse à des événements. Un événement est un type
particulier de propriété qui représente une situation à l’exécution, généralement
une action de l’utilisateur. Le code qui répond directement à un événement, ce
qu’on appelle un gestionnaire d’événement, est une procédure Pascal Objet. Les
sections suivantes expliquent comment :
• Générer un nouveau gestionnaire d’événement
• Générer le gestionnaire de l’événement par défaut d’un composant
• Rechercher un gestionnaire d’événement
• Associer un événement à un gestionnaire d’événement existant
• Associer des événements de menu à des gestionnaires d’événements
• Supprimer des gestionnaires d’événements

Génération d’un nouveau gestionnaire d’événement


Delphi peut créer le squelette de gestionnaires d’événements pour les fiches et
les autres composants. Pour créer un gestionnaire d’événement :
1 Sélectionnez un composant.
2 Cliquez dans la page Evénements de l’inspecteur d’objet. La page événement
de l’inspecteur d’objet affiche tous les événements définis pour le composant
sélectionné.
3 Sélectionnez l’événement de votre choix puis double-cliquez dans la colonne
valeur ou appuyez sur Ctrl+Entrée. Delphi génère le gestionnaire d’événement
dans l’éditeur de code et place le curseur dans le bloc begin...end.
4 A l’intérieur du bloc begin...end, entrez le code que vous voulez exécuter
lorsque l’événement se produit.

Génération du gestionnaire de l’événement par défaut d’un


composant
Certains composants ont un événement par défaut, celui que le composant a le
plus souvent besoin de gérer. Par exemple, l’événement par défaut d’un bouton
est OnClick. Pour créer un gestionnaire de l’événement par défaut, double-cliquez
sur le composant dans le concepteur de fiche, cela génère le squelette de la
procédure de gestion de l’événement et ouvre l’éditeur de code en plaçant le
curseur à l’intérieur du corps de la procédure où il ne vous reste plus qu’à
ajouter du code.
Certains composants n’ont pas d’événement par défaut. D’autres, comme le
biseau, n’ont pas du tout d’événement. D’autres composants encore peuvent
réagir différemment si vous double-cliquez dessus dans le concepteur de fiche.
Par exemple, plusieurs composants ouvrent un éditeur de propriété par défaut
ou une autre boîte de dialogue quand on double-clique dessus à la conception.

2-28 Guide du développeur


Utilisation des composants

Recherche de gestionnaires d’événements


Si vous avez généré le gestionnaire de l’événement par défaut d’un composant
en double-cliquant dessus dans le concepteur de fiche, vous pouvez revenir
dessus en recommençant. Double-cliquez sur le composant ; l’éditeur de code
s’ouvre, le curseur positionné sur le début du corps du gestionnaire
d’événement.
Pour rechercher le gestionnaire d’un événement qui n’est pas l’événement par
défaut :
1 Dans la fiche, sélectionnez le composant dont vous recherchez le gestionnaire
d’événement.
2 Dans l’inspecteur d’objet, cliquez sur l’onglet Evénements.
3 Sélectionnez l’événement dont vous recherchez le gestionnaire et double-
cliquez dans la colonne des valeurs. L’éditeur de code s’ouvre, le curseur
positionné sur le début du corps du gestionnaire d’événement.

Association d’un événement à un gestionnaire d’événement


existant
Vous pouvez réutiliser le code en écrivant des gestionnaires d’événements qui
gèrent plusieurs événements de composants. Par exemple, de nombreuses
applications proposent des turboboutons qui sont l’équivalent de commandes de
la barre des menus. Quand un bouton initie la même action qu’une commande
de menu, vous pouvez écrire un seul gestionnaire d’événement et l’affecter à
l’événement OnClick du bouton et de l’élément de menu.
Pour associer un événement à un gestionnaire d’événement existant :
1 Dans la fiche, sélectionnez le composant dont vous voulez gérer un
événement.
2 Dans la page Evénements de l’inspecteur d’objet, sélectionnez l’événement
auquel vous voulez attacher un gestionnaire.
3 Cliquez sur le bouton flèche vers le bas à côté de l’événement afin d’ouvrir
une liste des gestionnaires d’événements existants. La liste ne propose que les
gestionnaires d’événements écrits pour des événements portant le même nom
dans la même fiche. Sélectionnez dans la liste en cliquant sur un nom de
gestionnaire d’événement.
Cette manière de procéder est un moyen simple de réutiliser des gestionnaires
d’événements. Cependant, les listes d’actions constituent un outil plus puissant
permettant de centraliser l’organisation du code répondant à des commandes de
l’utilisateur. Pour davantage d’informations sur les listes d’actions, voir
“Utilisation des actions” à la page 5-41.

Utilisation du Pascal Objet avec la VCL 2-29


Utilisation des composants

Utilisation du paramètre Sender


Dans un gestionnaire d’événement, le paramètre Sender indique le composant qui
a reçu l’événement et qui a donc appelé le gestionnaire. Il est parfois pratique de
partager entre plusieurs composants un même gestionnaire d’événement qui doit
se comporter différemment selon le composant qui l’a appelé. Vous pouvez y
arriver en utilisant le paramètre Sender dans une instruction if...then...else. Par
exemple, le code suivant affiche le nom de l’application dans le titre d’une boîte
de dialogue uniquement si l’événement OnClick a été reçu par Button1.
procedure TMainForm.Button1Click(Sender: TObject);
begin
if Sender = Button1 then
AboutBox.Caption := 'A propos de ' + Application.Title
else AboutBox.Caption := '';
AboutBox.ShowModal;
end;

Affichage et codage d’événements partagés


Si des composants partagent des événements, vous pouvez afficher leurs
événements partagés dans l’inspecteur d’objet. Commencez par sélectionner les
composants en maintenant enfoncée la touche Maj et en cliquant dessus dans le
concepteur de fiche ; puis, choisissez l’onglet Evénements de l’inspecteur d’objet.
Dans la colonne des valeurs de l’inspecteur d’objet, vous pouvez alors créer un
nouveau gestionnaire d’événement ou affecter un gestionnaire d’événement
existant aux événements partagés.

Association d’événements de menu à des gestionnaires


d’événements
Le concepteur de menus de Delphi utilisés pour les composants MainMenu et
PopupMenu, vous permet de spécifier simplement des menus déroulants ou
surgissants dans une application. Pour que les menus fonctionnent, il faut
néanmoins que chaque élément de menu réponde à l’événement OnClick qui se
produit à chaque fois que l’utilisateur choisit l’élément de menu ou appuie sur
sa touche de raccourci. Cette section explique comment associer des gestionnaires
d’événements aux éléments de menu. Pour plus d’informations sur le concepteur
de menus et les composants associés, voir “Création et gestion de menus” à la
page 5-16.
Pour créer un gestionnaire d’événement pour un élément de menu :
1 Ouvrez le concepteur de menus en double-cliquant sur un objet MainMenu ou
PopupMenu.
2 Sélectionnez un élément de menu dans le concepteur de menus. Dans
l’inspecteur d’objet, vérifiez qu’une valeur est attribuée à la propriété Name de
l’élément.
3 Dans le concepteur de menus, double-cliquez sur l’élément de menu. Delphi
génère un gestionnaire d’événement dans l’éditeur de code et place le curseur
dans le bloc begin...end.

2-30 Guide du développeur


Emploi d’objets utilitaires

4 A l’intérieur du bloc begin...end, entrez le code qui doit être exécuté quand
l’utilisateur sélectionne la commande de menu.
Pour associer un élément de menu à un gestionnaire d’événement OnClick
existant :
1 Ouvrez le concepteur de menus en double-cliquant sur un objet MainMenu ou
PopupMenu.
2 Sélectionnez un élément de menu dans le concepteur de menus. Dans
l’inspecteur d’objet, vérifiez qu’une valeur est attribuée à la propriété Name de
l’élément.
3 Dans la page Evénements de l’inspecteur d’objet, cliquez sur le bouton flèche
vers le bas à côté de OnClick afin d’ouvrir une liste des gestionnaires
d’événements existants. La liste ne propose que les gestionnaires d’événements
écrits pour des événements OnClick dans la fiche. Sélectionnez dans la liste en
cliquant sur un nom de gestionnaire d’événement.

Suppression de gestionnaires d’événements


Quand vous supprimez un composant en utilisant le concepteur de fiche, Delphi
retire le composant de la déclaration de type de la fiche, mais les méthodes
associées ne sont pas retirées du fichier unité. En effet, ces méthodes sont peut-
être appelées par d’autres composants de la fiche. Vous pouvez supprimer
manuellement une méthode (donc un gestionnaire d’événement) mais si vous le
faites, vous devez supprimer la déclaration avancée de la méthode (dans la
section interface de l’unité) et son implémentation (dans la section
implementation). A défaut, vous obtiendrez une erreur de compilation lors de la
génération du projet.

Emploi d’objets utilitaires


La VCL propose divers objets non-visuels qui simplifient des tâches courantes de
programmation. Cette section décrit certains objets utilitaires :
• Création et gestion de listes
• Création et gestion de listes de chaînes
• Modification du registre Windows et des fichiers .INI
• Flux de données vers un disque dur ou d’autres périphériques de stockage

Utilisation des listes


Plusieurs objets de la VCL permettent de créer et gérer des listes :
• TList stocke une liste de pointeurs.
• TObjectList stocke une liste, gérée en mémoire, d’instances d’objets.
• TComponentList stocke une liste, gérée en mémoire, de composants (c’est-à-dire
d’instances de classes dérivées de TComponent).

Utilisation du Pascal Objet avec la VCL 2-31


Emploi d’objets utilitaires

• TQueue stocke une liste de pointeurs premier entré, premier sorti.


• TStack stocke une liste de pointeurs dernier entré, premier sorti.
• TObjectQueue stocke une liste d’objets premier entré, premier sorti.
• TObjectStack stocke une liste d’objets dernier entré, premier sorti.
• TClassList stocke une liste de types de classe.
• TCollection, TOwnedCollection et TCollectionItem stockent des collections indicées
d’éléments définis spécialement.
• TStringList stocke une liste de chaînes.
Pour davantage d’informations sur ces objets, consultez l’aide en ligne de la
VCL.

Utilisation des listes de chaînes


Souvent les applications ont besoin de gérer des listes de chaînes de caractères.
Par exemple, pour les éléments d’une boîte à options, les lignes d’un mémo, les
noms de fonte ou le nom des lignes et colonnes d’une grille de chaînes. La VCL
propose une interface pour toutes les listes de chaînes via un objet appelé
TStrings et son descendant TStringList. Outre les fonctionnalités concernant la
gestion de listes de chaînes, ces objets permettent une interopérabilité simple ;
vous pouvez ainsi modifier les lignes d’un mémo (qui sont une instance de
TStrings), puis utiliser ces lignes comme éléments d’une boîte à options
(également une instance de TStrings).
Une propriété liste de chaînes apparaît dans l’inspecteur d’objet avec TStrings
dans la colonne des valeurs. Double-cliquez sur TStrings pour ouvrir l’éditeur de
liste de chaînes qui vous permet de modifier, ajouter ou supprimer des chaînes.
Vous pouvez également manipuler les objets liste de chaînes à l’exécution pour
effectuer les opérations suivantes :
• Chargement et enregistrement de listes de chaînes
• Création d’une nouvelle liste de chaînes
• Manipulation des chaînes d’une liste
• Association d’objets à une liste de chaînes

Chargement et enregistrement de listes de chaînes


Les objets liste de chaînes disposent des méthodes SaveToFile et LoadFromFile qui
permettent de stocker une liste de chaînes dans un fichier texte ou de charger un
fichier texte dans une liste de chaînes. Chaque ligne du fichier texte correspond à
une chaîne de la liste. En utilisant ces méthodes, vous pouvez, par exemple,
créer un éditeur de texte simple en chargeant un fichier dans un composant
mémo ou enregistrer les listes d’éléments de boîtes à options.

2-32 Guide du développeur


Emploi d’objets utilitaires

L’exemple suivant charge une copie du fichier WIN.INI dans un champ mémo et
en fait une copie de sauvegarde nommée WIN.BAK.
procedure EditWinIni;
var
FileName: string;{ Stocke le nom du fichier }
begin
FileName := 'C:\WINDOWS\WIN.INI';{ Définit le nom du fichier }
with Form1.Memo1.Lines do
begin
LoadFromFile(FileName);{ Lit le fichier }
SaveToFile(ChangeFileExt(FileName, '.BAK'));{ Enregistre dans un fichier de sauvegarde
}
end;
end;

Création d’une nouvelle liste de chaînes


Habituellement, les listes de chaînes font partie de composants. Néanmoins, il est
parfois commode de créer des listes de chaînes autonomes qui n’ont pas de
composant associé, par exemple, pour stocker les chaînes d’une table de
référence. La manière de créer et de gérer une liste de chaînes varie selon que la
liste est une liste à court terme (construite, utilisée et détruite dans une même
routine) ou une liste à long terme (disponible jusqu’à l’arrêt de l’application).
Quel que soit le type de liste de chaînes créé, n’oubliez pas que c’est à vous de
libérer la liste quand vous n’en n’avez plus besoin

Listes de chaînes à court terme


Si vous n’utilisez une liste de chaînes que pour la durée d’une seule routine,
vous pouvez la créer, l’utiliser et la détruire au même emplacement. C’est la
méthode la plus fiable pour utiliser des objets liste de chaînes. Comme l’objet
liste de chaînes alloue la mémoire pour lui-même et pour ses chaînes, il est
important de protéger l’allocation en utilisant un bloc try...finally afin de garantir
que l’objet libère sa mémoire même si une exception a lieu.
1 Construire l’objet liste de chaînes.
2 Utiliser la liste de chaînes dans la partie try d’un bloc try...finally.
3 Libérer l’objet liste de chaînes dans la partie finally.
Le gestionnaire d’événement suivant répond au choix d’un bouton en
construisant un objet liste de chaînes, en l’utilisant puis en le détruisant :
procedure TForm1.Button1Click(Sender: TObject);
var
TempList: TStrings;{ déclare la liste }
begin
TempList := TStringList.Create;{ construit l’objet liste }
try
{ utilise la liste de chaînes }
finally
TempList.Free;{ détruit l’objet liste }
end;
end;

Utilisation du Pascal Objet avec la VCL 2-33


Emploi d’objets utilitaires

Listes de chaînes à long terme


Si la liste de chaînes doit être disponible tout au long de l’exécution de votre
application, vous devez construire la liste au démarrage de l’application et la
détruire avant la fermeture de l’application.
1 Dans le fichier en-tête de l’unité, ajoutez un champ de type TStrings à l’objet
fiche principale de l’application.
2 Créez un gestionnaire pour l’événement OnCreate de la fiche principale.
OnCreate étant l’événement par défaut d’une fiche, il suffit de double-cliquer
sur la fiche pour générer le squelette du gestionnaire d’événement. Ce
gestionnaire d’événement qui s’exécute avant l’affichage de la fiche à l’écran
lors de l’exécution doit créer une liste de chaînes et l’affecter au champ
déclaré dans la première étape.
3 Ecrivez un gestionnaire d’événement qui libère la liste de chaînes dans
l’événement OnDestroy de la fiche.
L’exemple suivant utilise une liste de chaînes à long terme pour stocker les clics
de la souris dans la fiche principale puis enregistre la liste dans un fichier avant
l’arrêt de l’application.
unit Unit1;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
{ Déclarations privées }
public
{ Déclarations publiques }
ClickList: TStrings;{ déclare le champ }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);


begin
ClickList := TStringList.Create;{ construit la liste }
end;

2-34 Guide du développeur


Emploi d’objets utilitaires

procedure TForm1.FormDestroy(Sender: TObject);


begin
ClickList.SaveToFile(ChangeFileExt(Application.ExeName, '.LOG'));{ enregistre la liste }
ClickList.Free;{ détruit l’objet liste }
end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;


Shift: TShiftState; X, Y: Integer);
begin
ClickList.Add(Format('Click at (%d, %d)', [X, Y]));{ ajoute une chaîne à la liste }
end;

end.

Manipulation des chaînes d’une liste


Les opérations couramment effectuées sur les listes de chaînes sont :
• Comptage des chaînes d’une liste
• Accès à une chaîne spécifique
• Recherche de la position d’une chaîne dans la liste
• Parcours des chaînes d’une liste
• Ajout d’une chaîne à une liste
• Déplacement d’une chaîne dans une liste
• Suppression d’une chaîne d’une liste
• Copie de la totalité d’une liste de chaînes

Comptage des chaînes d’une liste


La propriété en lecture seule Count renvoie le nombre de chaînes dans la liste.
Comme les listes de chaînes utilisent des indices de base zéro, Count correspond
à l’indice de la dernière chaîne plus un.

Accès à une chaîne spécifique


La propriété tableau Strings contient les chaînes de la liste, référencées par un
indice de base zéro. Comme Strings est la propriété par défaut des listes de
chaînes, vous pouvez omettre l’identificateur Strings pour accéder à la liste, donc
StringList1.Strings[0] := 'Première chaîne.';
est équivalent à
StringList1[0] := 'Première chaîne.';

Recherche de la position d’une chaîne dans la liste


Pour rechercher une chaîne dans une liste de chaînes, utilisez la méthode
IndexOf. IndexOf renvoie l’indice de la première chaîne de la liste qui correspond
au paramètre transmis et renvoie –1 si la chaîne transmise en paramètre n’est
pas trouvée. IndexOf recherche uniquement une correspondance exacte ; si vous
voulez obtenir des chaînes de correspondance partielle, vous devez parcourir la
liste de chaînes.

Utilisation du Pascal Objet avec la VCL 2-35


Emploi d’objets utilitaires

Vous pouvez, par exemple, utiliser IndexOf pour déterminer si un nom de fichier
donné se trouve dans les éléments (Items) d’une boîte liste :
if FileListBox1.Items.IndexOf('WIN.INI') > -1 ...

Parcours des chaînes d’une liste


Pour parcourir les chaînes d’une liste, utilisez une boucle for allant de zéro à
Count – 1.
L’exemple suivant convertit en majuscules chaque chaîne d’une boîte liste.
procedure TForm1.Button1Click(Sender: TObject);
var
Index: Integer;
begin
for Index := 0 to ListBox1.Items.Count - 1 do
ListBox1.Items[Index] := UpperCase(ListBox1.Items[Index]);
end;

Ajout d’une chaîne à une liste


Pour ajouter une chaîne à la fin d’une liste de chaînes, utilisez la méthode Add
en lui transmettant en paramètre la nouvelle chaîne. Pour insérer une chaîne
dans la liste, appelez la méthode Insert en lui transmettant deux paramètres : la
chaîne et l’indice à laquelle elle doit être placée. Si, par exemple, vous voulez
placez la chaîne "Trois" en troisième position dans la liste, utilisez :
Insert(2, 'trois');
Pour ajouter à une liste les chaînes d’une liste, appelez AddStrings:
StringList1.AddStrings(StringList2); { Ajoute à StringList1 les chaînes de StringList2 }

Déplacement d’une chaîne dans une liste


Pour déplacer une chaîne dans une liste de chaînes, appelez la méthode Move en
lui transmettant deux paramètres : l’indice en cours de la chaîne et son nouvel
indice. Par exemple, pour déplacer la troisième chaîne de la liste en cinquième
position, utilisez :
Move(2, 4)

Suppression d’une chaîne d’une liste


Pour supprimer une chaîne d’une liste de chaînes, appelez la méthode Delete de
la liste en lui transmettant l’indice de la chaîne à supprimer. Si vous ne
connaissez pas l’indice de la chaîne à supprimer, utilisez la méthode IndexOf
pour le déterminer. Pour supprimer toutes les chaînes de la liste, utilisez la
méthode Clear.
L’exemple suivant utilise IndexOf et Delete pour trouver et supprimer une chaîne.
with ListBox1.Items do
begin
if IndexOf('bureaucratie') > -1 then
Delete(IndexOf('bureaucratie'));
end;

2-36 Guide du développeur


Emploi d’objets utilitaires

Copie de la totalité d’une liste de chaînes


Vous pouvez utiliser la méthode Assign pour copier les chaînes d’une liste source
vers une liste de destination en remplaçant le contenu de la liste de destination.
Pour ajouter les chaînes sans remplacer la liste de destination, utilisez la
méthode AddStrings.Par exemple :
Memo1.Lines.Assign(ComboBox1.Items); { remplace les chaînes existantes }
copie les lignes d’une boîte à options dans un mémo (en écrasant le contenu du
mémo), alors que :
Memo1.Lines.AddStrings(ComboBox1.Items); { ajoute les chaînes à la fin }
ajoute au mémo les lignes de la boîte à options.
Quand vous effectuez une copie locale d’une liste de chaînes, utilisez la méthode
Assign. Si vous affectez simplement une variable liste de chaînes à une autre :
StringList1 := StringList2;
l’objet liste de chaîne initial est perdu, ce qui peut donner des résultats
imprévisibles.

Association d’objets à une liste de chaînes


Outre les chaînes stockées dans sa propriété Strings, une liste de chaînes peut
gérer des références à des objets dans sa propriété Objects. Comme Strings, Objects
est un tableau d’indice zéro. Le plus souvent, Objects sert à associer des bitmaps
aux chaînes dans des contrôles dessinés par le propriétaire.
Utilisez la méthode AddObject ou InsertObject pour ajouter en une seule étape la
chaîne et son objet associé à la liste. IndexOfObject renvoie l’indice de la première
chaîne de la liste associée à l’objet spécifié. Les méthodes comme Delete, Clear et
Move agissent à la fois sur les chaînes et les objets ; ainsi, la suppression d’une
chaîne supprime également l’éventuel objet correspondant.
Pour associer un objet à une chaîne existant, affectez l’objet à la propriété Objects
pour le même indice. Vous ne pouvez pas ajouter d’objet sans ajouter une chaîne
correspondante.

Le registre et les fichiers INI Windows


Le registre système Windows est une base de données hiérarchique dans laquelle
les applications stockent des informations de configuration. L’objet VCL TRegistry
propose les méthodes permettant de lire et d’écrire dans le registre.
Avant Windows 95, la plupart des applications stockaient les informations de
configuration dans des fichiers d’initialisation, utilisant généralement l’extension
.INI. La VCL propose des objets permettant de faciliter la maintenance et la
migration de programmes utilisant les fichiers INI. Employez
• TRegistry pour utiliser le registre.
• TIniFile ou TMemIniFile pour utiliser des fichiers INI Windows 3.x.

Utilisation du Pascal Objet avec la VCL 2-37


Utilisation de modules de données et de modules de données distants

• TRegistryIniFile pour utiliser à la fois le registre et les fichiers INI.


TRegistryIniFile dispose de propriétés et méthodes similaires à celles de TIniFile
mais il lit et écrit dans le registre système. En utilisant une variable de type
TCustomIniFile (l’ancêtre commun à TIniFile, TMemIniFile et TRegistryIniFile),
vous pouvez écrire un code générique qui accède soit au registre, soit à un
fichier INI, selon l’endroit où il est utilisé.

Utilisation des flux


Utilisez des objets flux spécialisés pour lire, écrire dans un support de stockage.
Chaque descendant de TStream implémente des méthodes pour accéder à un
support de stockage particulier : fichier disque, mémoire dynamique. Les
descendants de TStream sont TFileStream, TStringStream, TMemoryStream,
TBlobStream et TWinSocketStream. Outre les méthodes de lecture et d’écriture, ces
objets permettent aux applications de se positionner de manière arbitraire dans le
flux. Les propriétés de TStream donnent des informations sur le flux, comme sa
taille ou la position en cours.

Utilisation de modules de données et de modules de


données distants
Un module de données ressemble à une fiche spéciale qui ne contient que des
composants non-visuels. Tous les composants d’un module de données peuvent
être placés dans des fiches ordinaires avec des contrôles visuels. Les modules de
données constituent un moyen d’organisation utile quand vous envisagez de
réutiliser des groupes d’objets de base de données ou système ou si vous
souhaitez isoler les parties d’une application qui gèrent la connexion aux bases
de données ou les règles de fonctionnement.
Il existe deux types de modules de données : standard et distant. Pour créer une
application à un ou deux niveaux, utilisez un module de données standard. Si
vous utilisez les versions Client/Serveur ou Entreprise de Delphi pour créer une
application multiniveau, vous pouvez ajouter un module de données distant à
votre serveur d’applications ; voir “Ajout d’un module de données distant à un
projet serveur d’application” à la page 2-40.

Création et modification de modules de données


Pour créer un nouveau module de données, choisissez Fichier|Nouveau et
double-cliquez sur Module de données. Delphi ouvre un module de données
vide dans le concepteur de module de données, affiche le fichier unité du
nouveau module dans l’éditeur de code et ajoute le module au projet en cours.
Quand vous rouvrez un module de données existant, Delphi affiche ses
composants dans le concepteur de module de données.

2-38 Guide du développeur


Utilisation de modules de données et de modules de données distants

Le concepteur de module de données est divisé en deux volets. Le volet de


gauche affiche une vue arborescente hiérarchique des composants du module. Le
volet de droite comporte deux onglets : Composants et Diagramme données. La
page Composants affiche les composants comme ils apparaîtraient dans une
fiche. La page Diagramme données montre une représentation graphique des
relations internes entre les composants, par exemple les liaisons maître-détail ou
les champs de référence.
Figure 2.5 Un module de données simple

Vous pouvez ajouter des composants à un module de données en les


sélectionnant dans la palette des composants puis en cliquant dans l’arborescence
ou la vue Composants du concepteur de module de données. Quand un
composant est sélectionné dans le concepteur de module de données, vous
pouvez modifier ses propriétés dans l’inspecteur d’objet comme si le composant
était placé dans une fiche. Pour davantage d’informations sur le concepteur de
module de données, voir l’aide en ligne.

Création de règles de gestion dans un module de données


Dans le fichier unité d’un module de données, vous pouvez écrire des méthodes,
y compris des gestionnaires d’événements pour les composants du module, ainsi
que des routines globales qui encapsulent des règles de gestion. Vous pouvez
par exemple, écrire une procédure pour effectuer un récapitulatif mensuel,
trimestriel ou annuel. Vous pouvez appeler la procédure depuis le gestionnaire
d’événement d’un composant du module de données ou depuis toute unité
utilisant ce module.

Accès à un module de données depuis une fiche


Pour associer des contrôles visuels d’une fiche à un module de données, vous
devez tout d’abord ajouter le module de données à la clause uses de la fiche.
Pour ce faire, vous pouvez procéder de plusieurs manières :
• Ouvrez le fichier unité de la fiche dans l’éditeur de code et ajoutez le nom du
module de données à la clause uses dans la section interface.

Utilisation du Pascal Objet avec la VCL 2-39


Utilisation du référentiel d’objets

• Choisissez Fichier|Utiliser unité puis entrez le nom d’un module ou


choisissez-le dans la boîte liste de la boîte de dialogue Utiliser l’unité.
• Double-cliquez sur un composant TTable ou TQuery dans le module de
données pour ouvrir l’éditeur de champs. Depuis l’éditeur de champs, faites
glisser des champs dans la fiche, Delphi vous demande alors de confirmer
l’ajout de ce module dans la clause uses de la fiche puis crée des contrôles
(par exemple, des boîtes de saisie) pour chaque champ.

Ajout d’un module de données distant à un projet


serveur d’application
Certaines versions de Delphi vous permettent d’ajouter des modules de données
distants à des projets de serveur d’applications. Un module de données distant
dispose d’une interface à laquelle les clients d’une application multiniveau
peuvent accéder au travers d’un réseau. Pour ajouter un module de données
distant à un projet, choisissez Fichier|Nouveau, sélectionnez la page Multiniveau
dans la boîte de dialogue Nouveaux éléments et double-cliquez sur le type de
module souhaité (Module de données distant, Module de données MTS ou
Module de données CORBA) pour ouvrir l’expert module de données distant.
Une fois le module de données distant ajouté à un projet, vous pouvez l’utiliser
comme un module de données standard.
Pour davantage d’informations sur les applications de base de données
multiniveaux, voir chapitre 14, “Création d’applications multiniveaux.”

Utilisation du référentiel d’objets


Le référentiel d’objets (Outils|Référentiel) vous permet facilement de partager ou
de copier des fiches, des boîtes de dialogue ou des modules de données. Il
propose également des modèles de projet comme point de départ pour de
nouveaux projets et des experts qui guident l’utilisateur dans la création de
fiches ou de projets. Le référentiel est stocké dans DELPHI32.DRO (placé par
défaut dans le répertoire BIN), c’est un fichier texte qui contient des références
aux éléments apparaissant dans le référentiel et dans la boîte de dialogue
Nouveaux éléments.

Partage d’éléments dans un projet


Il est également facile de partager des éléments à l’intérieur d’un projet sans avoir
à les ajouter au référentiel d’objets : quand vous ouvrez la boîte de dialogue
Nouveaux éléments (Fichier|Nouveau), l’onglet d’une des pages porte le nom de
votre projet. Cette page énumère toutes les fiches, boîtes de dialogue et modules
de données de votre projet. Vous pouvez alors dériver un nouvel élément d’un
élément existant et le personnaliser si nécessaire.

2-40 Guide du développeur


Utilisation du référentiel d’objets

Ajout d’éléments au référentiel d’objets


Vous pouvez ajouter vos propres projets, fiches, cadres et modules de données à
ceux qui existent déjà dans le référentiel d’objets. Pour ajouter un élément au
référentiel d’objets :
1 Si l’élément est un projet ou dans un projet, ouvrez le projet.
2 Pour un projet, choisissez Projet|Ajouter au référentiel. Pour une fiche ou un
module de données, cliquez sur l’élément avec le bouton droit de la souris
puis choisissez Ajouter au référentiel.
3 Entrez une description, un titre et un auteur.
4 Décidez dans quelle page cet élément doit apparaître dans la boîte de
dialogue Nouveaux éléments, entrez ou sélectionnez la page dans la boîte à
options Page. Si vous entrez un nom de page inexistant, Delphi crée une
nouvelle page.
5 Choisissez Parcourir pour sélectionner une icône représentant l’objet dans le
référentiel d’objets.
6 Choisissez OK.

Partage d’objets par une équipe de développement


Vous pouvez partager des objets dans un groupe de travail ou une équipe de
développement en utilisant un référentiel accessible depuis un réseau. Pour
utiliser un référentiel partagé, tous les membres de l’équipe doivent sélectionner
le même répertoire de Référentiel partagé dans la boîte de dialogue Options
d’environnement :
1 Choisissez Outils|Options d’environnement.
2 Dans la page Préférences, repérez le volet Référentiel partagé. Dans le volet
boîte de saisie Répertoire, entrez le nom du répertoire où doit se trouver le
référentiel partagé. Assurez-vous que le répertoire spécifié est bien accessible
pour tous les membres de l’équipe.
Lors de l’ajout du premier élément au référentiel, Delphi crée, s’il n’existe pas
déjà, un fichier DELPHI32.DRO dans le répertoire spécifié par Référentiel partagé.

Utilisation d’un élément du référentiel d’objets dans


un projet
Pour accéder aux éléments du référentiel d’objets, choisissez Fichier|Nouveau.
La boîte de dialogue Nouveaux éléments apparaît et affiche tous les éléments du
référentiel d’objets. Selon le type d’élément que vous souhaitez utiliser, il y
jusqu’à trois options pour ajouter un élément à votre projet :
• Copier
• Hériter
• Utiliser

Utilisation du Pascal Objet avec la VCL 2-41


Utilisation du référentiel d’objets

Copie d’un élément


Sélectionnez Copier pour obtenir une réplique exacte de l’élément sélectionné et
ajouter la copie à votre projet. Les modifications ultérieures de l’élément du
référentiel d'objets ne sont pas répercutées sur votre copie. De même, les
modifications apportées à la copie n’affectent pas l'élément original dans le
référentiel d’objets.
Copier est la seule option possible pour les modèles de projet.

Héritage d’un élément


Sélectionnez Hériter pour dériver une nouvelle classe de l’élément sélectionné
dans le référentiel d’objets et ajouter la nouvelle classe à votre projet. Quand
vous recompilez votre projet, toutes les modifications apportées à l’élément du
référentiel d’objets sont reportées dans votre classe dérivée. Les modifications
faites dans la classe dérivée n’affectent pas l’élément partagé du référentiel
d’objets.
Hériter est une option proposée pour les fiches, les boîtes de dialogue et les
modules de données, mais pas pour les modèles de projet. C’est la seule option
utilisable pour réutiliser les éléments du projet en cours.

Utilisation d’un élément


Sélectionnez Utiliser quand vous voulez que l’objet sélectionné fasse lui-même
partie de votre projet. Les modifications faites à l’élément apparaissent dans tous
les projets dans lesquels l’élément a été ajouté en utilisant l’option Hériter ou
Utiliser. Soyez prudent si vous choisissez cette option.
L’option Utiliser est disponible pour les fiches, les boîtes de dialogue et les
modules de données.

Utilisation de modèles de projet


Les modèles de projet sont des projets préfabriqués que vous pouvez utiliser
comme point de départ pour la création de vos projets. Pour créer un nouveau
projet à partir d’un modèle :
1 Choisissez Fichier|Nouveau pour afficher la boîte de dialogue Nouveaux
éléments.
2 Choisissez l’onglet Projets.
3 Sélectionnez le modèle de projet souhaité et choisissez OK.
4 Dans la boîte de dialogue Sélection du répertoire, spécifiez le répertoire des
fichiers du nouveau projet.
Delphi copie les fichiers du modèle dans le répertoire spécifié, où vous pouvez
ensuite les modifier. Le modèle de projet initial n’est pas affecté par vos
modifications.

2-42 Guide du développeur


Ajout de composants personnalisés à l’EDI

Modification d’éléments partagés


Si vous modifiez un élément du référentiel d’objets, vos modifications affectent
tous les projets qui ultérieurement utilisent l’élément mais également tous les
projets existants qui ont ajouté l’élément en utilisant les options Utiliser ou
Hériter. Pour éviter de propager des modifications à d’autres projets, vous avez
plusieurs solutions :
• Copier l’élément et le modifier uniquement dans le projet en cours.
• Copier l’élément dans le projet en cours, le modifier puis l’ajouter au
référentiel sous un autre nom.
• Créer un composant, une DLL, un modèle de composant ou un cadre à partir
de l’élément. Si vous créez un composant ou une DLL, vous pouvez le
partager avec d’autres développeurs.

Spécification d’un projet par défaut, d’une nouvelle


fiche et de la fiche principale
Par défaut, quand vous choisissez Fichier|Nouvelle application ou Fichier|
Nouvelle fiche, Delphi affiche une fiche vide. Vous pouvez changer ce
comportement en reconfigurant le référentiel :
1 Choisissez Outils|Référentiel
2 Si vous voulez spécifier un projet par défaut, sélectionnez la page Projets et
choisissez un élément dans Objets. Sélectionnez ensuite la case à cocher
Nouveau projet.
3 Pour spécifier une fiche par défaut, sélectionnez une fiche dans Objets. Pour
spécifier la nouvelle fiche par défaut, (Fiche|Nouvelle fiche), sélectionnez la
case à cocher Nouvelle fiche. Pour spécifier la nouvelle fiche principale par
défaut des nouveaux projets, sélectionnez la case à cocher Fiche principale.
4 Choisissez OK.

Ajout de composants personnalisés à l’EDI


Vous pouvez installer des composants personnalisés (conçus par vous ou acquis
séparément) dans la palette des composants et les utiliser dans vos applications.
Pour écrire un composant, voir partie IV, “Création de composants
personnalisés”. Pour installer un composant existant, voir “Installation de
paquets de composants” à la page 9-6.

Utilisation du Pascal Objet avec la VCL 2-43


2-44 Guide du développeur
Chapitre

Sujets de programmation
Chapter 3
3
généraux
Ce chapitre décrit les principes de base des formes courantes de programmation
dans Delphi :
• Gestion des exceptions
• Utilisation des interfaces
• Utilisation des chaînes
• Utilisation des fichiers

Gestion des exceptions


Delphi propose un mécanisme permettant de fiabiliser les applications en gérant
les erreurs d’une manière systématique. La gestion des exceptions permet à
l’application de gérer les erreurs si c’est possible et, si c’est nécessaire, de se
fermer sans perdre de données ou de ressources. Les conditions d’erreurs sont
indiquées dans Delphi par des exceptions. Cette section décrit les aspects
suivants de l’utilisation des exceptions pour créer des applications fiables :
• Protection des blocs de code
• Protection de l’allocation de ressources
• Gestion des exceptions RTL
• Gestion des exceptions des composants
• Utilisation de TApplication.HandleException
• Exceptions silencieuses
• Définition d’exceptions personnalisées

Sujets de programmation généraux 3-1


Gestion des exceptions

Protection des blocs de code


Pour rendre vos applications plus fiables, votre code doit reconnaître les
exceptions quand elles se produisent et y répondre. Si vous ne spécifiez pas de
réponse, l’application affiche une boîte message décrivant l’erreur. Votre travail
consiste donc à déterminer où les erreurs peuvent se produire et à définir des
réponses à ces erreurs, en particulier dans les situations où une erreur peut
entraîner une perte de données ou de ressources système.
Quand vous créez la réponse à une exception, vous le faites pour des blocs de
code. Quand une suite d’instructions nécessitent toutes le même type de réponse
aux erreurs, vous pouvez les regrouper dans un bloc et définir des réponses aux
erreurs qui portent sur la totalité du bloc.
Les blocs disposant de réponses spécifiques à des exceptions sont appelés des
blocs protégés car ils sont capables de se prémunir contre les erreurs qui, sinon,
provoquent l’arrêt de l’application ou la perte de données.
Pour protéger des blocs de code, vous devez maîtriser :
• La réponse aux exceptions
• Les exceptions et le flux d’exécution
• La réponse aux exceptions imbriquées

Réponse aux exceptions


Quand une condition d’erreur se produit, l’application déclenche une exception :
elle crée un objet exception. Une fois l’exception déclenchée, votre application
peut exécuter du code de nettoyage, gérer l’exception ou faire les deux.
• Exécution de code de nettoyage :la manière la plus simple de répondre à une
exception est de garantir que du code de nettoyage est bien exécuté. Ce type
de réponse ne corrige pas la situation qui a provoqué l’erreur mais vous
assure que l’application ne laisse pas l’environnement dans un état instable.
Généralement, vous utiliserez ce type de réponse pour garantir que
l’application libère bien les ressources allouées quelle que soit l’erreur qui a eu
lieu.
• Gestion d’une exception : c’est une réponse spécifique à un type particulier
d’exception. La gestion d’une exception supprime la condition d’erreur et
détruit l’objet exception, ce qui permet à l’application de poursuivre son
exécution. Normalement, vous définissez des gestionnaires d’exceptions pour
permettre aux applications de se rétablir après des erreurs et de poursuivre
l’exécution. Vous pouvez gérer des types divers d’exceptions, par exemple des
tentatives d’ouverture d’un fichier inexistant, l’écriture dans un disque plein
ou des débordements dans des calculs. Certaines d’entres elles, comme
“Fichier non trouvé” sont faciles à corriger et à reprendre tandis que d’autres,
comme l’insuffisance de mémoire, sont plus difficiles à corriger pour
l’application ou l’utilisateur.

3-2 Guide du développeur


Gestion des exceptions

Exceptions et contrôle d’exécution


Le Pascal Objet permet d’inclure facilement la gestion des erreurs dans les
applications car les exceptions ne rentrent pas dans le déroulement normal du
code. En fait, en déplaçant la vérification et la gestion des erreurs hors du
déroulement principal de vos algorithmes, les exceptions peuvent simplifier le
code que vous écrivez.
Quand vous déclarez un bloc protégé, vous définissez des réponses spécifiques
aux exceptions qui peuvent se produire à l’intérieur du bloc. Quand une
exception se produit dans le bloc, l’exécution sort du bloc, passe immédiatement
à la réponse que vous avez défini.
Exemple Le code suivant comporte un bloc protégé. Si une exception se produit dans le
bloc protégé, l’exécution passe à la partie gestion d’exception qui génère un bip
sonore. L’exécution se poursuit hors du bloc :
...
try{ début du bloc protégé }
Font.Name := 'Courier';{ si une exception se produit ... }
Font.Size := 24;{ ...dans l’une des ces instructions... }
Color := clBlue;
except{ ...l’exécution passe ici }
on Exception do MessageBeep(0);{ l’exception est gérée par un bip sonore }
end;
...{ l’exécution reprend ici, hors du bloc protégé}

Réponses à des exceptions imbriquées


Votre code définit des réponses aux exceptions se produisant dans des blocs.
Comme le Pascal permet d’imbriquer des blocs les uns dans les autres, vous
pouvez même personnaliser les réponses à l’intérieur de blocs qui personnalisent
déjà les réponses.
Dans le cas le plus simple vous pouvez, par exemple protéger l’allocation d’une
ressource et, à l’intérieur de ce bloc protégé, définir des blocs qui allouent et
protègent d’autres ressources. Conceptuellement, cela peut se représenter de la
manière suivante :
( allouer la première ressource )
try
( allouer la seconde ressource )
try
bloc protégé
bloc protégé

imbriqué

( code utilisant les deux ressources


finally
( libérer la seconde ressource )
end;
finally
( libérer la première resource )
end;

Sujets de programmation généraux 3-3


Gestion des exceptions

Vous pouvez également utiliser des blocs imbriqués afin de définir une gestion
locale, pour des exceptions spécifiques, qui redéfinit la gestion du bloc
environnant. Conceptuellement, cela peut se représenter de la manière suivante :
try
( code protégé)
bloc de gestion
des exceptions

try
bloc de gestion
des exceptions

imbriqué

( code spécialement protégé


except
( gestion des exceptions locale)
end;
except
( gestion des exception globale)
end;
Vous pouvez également mélanger différentes sortes de blocs de réponse aux
exceptions, par exemple en imbriquant la protection de ressources dans des blocs
de gestion d’exceptions ou l’inverse.

Protection de l’allocation de ressources


Pour garantir la fiabilité de vos applications, un élément essentiel est de s’assurer
lors de l’allocation des ressources que vous les libérez même si une exception a
lieu. Ainsi, quand votre application alloue de la mémoire, vous devez vous
assurer qu’elle la libère bien. De même, si elle ouvre un fichier, vous devez vous
assurer que le fichier est bien fermé ultérieurement.
N’oubliez pas que votre code n’est pas seul à générer des exceptions. L’appel
d’une routine RTL ou d’un autre composant de votre application peut aussi
déclencher une exception. Votre code doit s’assurer que même dans ces
situations les ressources allouées sont correctement libérées.
Pour protéger les ressources de manière fiable, vous devez savoir :
• Quelles ressources doivent être protégées?
• Créer un bloc de protection de ressource

Quelles ressources doivent être protégées?


Dans une situation normale, vous pouvez garantir qu’une application libère les
ressources allouées en spécifiant simplement le code d’allocation et de libération
des ressources. Quand des exceptions ont lieu, vous devez vous assurer que
l’application exécute quand même le code de libération des ressources.
Certaines ressources courantes doivent toujours être libérées :
• Les fichiers
• La mémoire
• Les ressources Windows
• Les objets.

3-4 Guide du développeur


Gestion des exceptions

Exemple Le gestionnaire d’événement suivant alloue de la mémoire puis génère une


erreur : il ne libère donc jamais la mémoire :
procedure TForm1.Button1Click(Sender: TComponent);
var
APointer: Pointer;
AnInteger, ADividend: Integer;
begin
ADividend := 0;
GetMem(APointer, 1024);{ allouer 1Ko de mémoire }
AnInteger := 10 div ADividend;{ cela provoque une erreur }
FreeMem(APointer, 1024);{ Le code n’arrive jamais ici }
end;
Toutes les erreurs ne sont pas aussi évidentes, mais cet exemple illustre un
principe important : quand l’erreur de division par zéro se produit, l’exécution
sort du bloc, ainsi l’instruction FreeMem n’est donc jamais exécutée pour libérer
la mémoire.
Pour être certain que FreeMem a la possibilité de libérer le bloc de mémoire
alloué par GetMem, vous devez placer le code dans un bloc de protection de
ressource.

Création d’un bloc de protection de ressource


Pour garantir que des ressources allouées sont effectivement libérées, même en
cas d’exception, vous devez intégrer le code utilisant la ressource dans un bloc
protégé, le code de libération de la ressource étant placé dans une partie spéciale
du bloc. Voici l’organisation générale d’une allocation protégée de ressource :
{ allocation de la ressource }
try
{ instructions utilisant la ressource }
finally
{ libération de la ressource }
end;
Le secret de l’instruction try..finally, c’est que l’application exécute toujours les
instructions placées dans la partie finally du bloc même quand des exceptions se
produisent dans le bloc protégé. Si du code (même une routine appelée) de la
partie try du bloc déclenche une exception, l’exécution s’interrompt là. Quand un
gestionnaire d’exception est trouvé, l’exécution se poursuit dans la partie finally,
qui est appelée le "code de nettoyage". Une fois la partie finally exécutée, le
gestionnaire d’exception est appelé. Quand il n’y a pas d’exception, le code de
nettoyage est exécuté dans l’ordre normal après toutes les instructions de la
partie try.
Exemple Le gestionnaire d’événement défini par le code suivant alloue de la mémoire et
génère une erreur mais libère quand même la mémoire allouée :
procedure TForm1.Button1Click(Sender: TComponent);
var
APointer: Pointer;
AnInteger, ADividend: Integer;

Sujets de programmation généraux 3-5


Gestion des exceptions

begin
ADividend := 0;
GetMem(APointer, 1024);{ allouer 1Ko de mémoire }
try
AnInteger := 10 div ADividend;{ cela génère une erreur }
finally
FreeMem(APointer, 1024);{ malgré l’erreur, l’exécution reprend ici }
end;
end;
Les instructions placées dans le code de conclusion ne dépendent pas de
l’apparition d’une exception. Si les instructions de la partie try ne déclenchent
pas d’exceptions, l’exécution se poursuit quand même dans le code de conclusion
placé dans la partie finally.

Gestion des exceptions RTL


Quand vous écrivez du code qui appelle les routines de la bibliothèque
d’exécution (RTL), comme les fonctions mathématiques ou les procédures de
gestion de fichier, la RTL informe votre application des erreurs par le biais
d’exceptions. Par défaut, les exceptions RTL génèrent un message affiché par
l’application. Vous pouvez définir vos propres gestionnaires pour gérer
différemment les exceptions RTL.
Il existe également des exceptions silencieuses qui, par défaut, n’affichent pas de
message.
La gestion des exceptions RTL nécessite la maîtrise des sujets suivants :
• Qu’est-ce qu’une exception RTL ?
• Création d’un gestionnaire d’exception
• Instructions de gestion des exceptions
• Utilisation de l’instance d’exception
• Portée des gestionnaires d’exceptions
• Spécification du gestionnaire d’exception par défaut
• Gestion des classes d’exceptions
• Redéclenchement de l’exception

Qu’est-ce qu’une exception RTL ?


Les exceptions de la bibliothèque d’exécution sont définies dans l’unité SysUtils,
elles dérivent toutes d’un type d’objet exception générique appelé Exception.
Exception définit la chaîne du message, affiché par défaut, par les exceptions RTL.
Il existe plusieurs sortes d’exceptions déclenchées par la RTL décrites dans le tableau
suivant.

Tableau 3.1 Exceptions RTL


Type d’erreur Cause Signification
Entrées/Sorties Erreur d’accès à un La plupart des exceptions d’E/S sont liées à
fichier ou à un des codes d’erreur renvoyés par Windows lors
périphérique d’E/S. de l’accès à un fichier.

3-6 Guide du développeur


Gestion des exceptions

Tableau 3.1 Exceptions RTL


Type d’erreur Cause Signification
Tas Erreur d’utilisation Les erreurs de tas se produisent quand il n’y a
de la mémoire pas assez de mémoire disponible ou
dynamique. lorsqu’une application libère un pointeur qui
pointe hors du tas.
Calcul entier Opération illégale sur Ces erreurs sont la division par zéro, les
des expressions de nombres et les expressions hors étendue et les
type entier. débordements.
Calcul à virgule Opération illégale sur Les erreurs dans les calculs à virgule flottante
flottante des expressions de proviennent du coprocesseur ou de
type réel. l’émulateur logiciel. Ces erreurs sont les
instructions incorrectes, la division par zéro et
les débordements.
Transtypage Transtypage incorrect Les objets ne peuvent être transtypés que dans
avec l’opérateur as. des types compatibles.
Conversion Conversion de type Les fonctions de conversion de type comme
incorrect. IntToStr, StrToInt ou StrToFloat déclenchent
des exceptions de conversion quand le
paramètre ne peut être converti dans le type
souhaité.
Matérielle Condition du Les exceptions matérielles indiquent que le
système. processeur ou l’utilisateur a généré une
condition d’erreur ou une interruption, par
exemple une violation d’accès, un
débordement de pile ou une interruption
clavier.
Variant Coercition de type Des erreurs peuvent se produire dans des
illégale. expressions faisant référence à des variants
quand le variant ne peut être forcé dans un
type compatible.

Pour une liste des types d’exception RTL, voir l’unité SysUtils dans le système
d’aide.

Création d’un gestionnaire d’exception


Un gestionnaire d’exception est le code qui gère une exception spécifique ou
toutes les exceptions se produisant dans un bloc de code protégé.
Pour définir un gestionnaire d’exception, incorporez le code à protéger dans un
bloc de gestion des exceptions et spécifiez les instructions de gestion des
exceptions dans la partie except du bloc. Le code suivant est le squelette d’un
bloc de gestion des exceptions standard :
try
{ instructions à protéger }
except
{ instructions de gestion des exceptions }
end;

Sujets de programmation généraux 3-7


Gestion des exceptions

L’application exécute les instructions de la partie except uniquement si une


exception se produit lors de l’exécution des instructions placées dans la partie
try. L’exécution des instructions de la partie try inclut également les routines
appelées par le code la partie try. Cela signifie que si la partie try appelle une
routine ne définissant pas son propre gestionnaire d’exception, l’exécution
revient sur le bloc de gestion des exceptions qui gère l’exception.
Quand une instruction de la partie try déclenche une exception, l’exécution passe
immédiatement à la partie except où elle passe en revue les instructions de
gestion d’exception spécifiées ou les gestionnaires d’exceptions, jusqu’à trouver
un gestionnaire s’appliquant à l’exception en cours.
Quand l’application a trouvé un gestionnaire d’exception qui gère l’exception,
elle exécute l’instruction puis détruit automatiquement l’objet exception.
L’exécution reprend ensuite après la fin du bloc en cours.

Instructions de gestion des exceptions


Chaque instruction on dans la partie except d’un bloc try..except définit le code
gérant un type particulier d’exception. Les instructions de gestion des exceptions
ont la forme suivante :
on <type d’exception> do <instruction>;
Exemple Vous pouvez ainsi définir un gestionnaire d’exception pour la division par zéro
qui définit un résultat par défaut :
function GetAverage(Sum, NumberOfItems: Integer): Integer;
begin
try
Result := Sum div NumberOfItems;{ gère le cas normal }
except
on EDivByZero do Result := 0;{ gère l’exception si c’est nécessaire }
end;
end;
Remarquez que cette organisation est plus claire que de placer un test de nullité
à chaque appel de la fonction. Voici la même fonction écrite sans tirer profit des
exceptions :
function GetAverage(Sum, NumberOfItems: Integer): Integer;
begin
if NumberOfItems <> 0 then{ tester ssytématiquement }
Result := Sum div NumberOfItems{ utiliser le calcul normal }
else Result := 0;{ gérer le cas exceptionnel }
end;
La différence entre ces deux fonctions résume très bien les différences entre une
programmation utilisant les exceptions et une programmation qui ne les utilise
pas. Cet exemple est relativement simple mais vous pouvez imaginer des calculs
plus complexes faisant intervenir des centaines d’étapes, chacune pouvant
échouer si un des paramètres parmi une douzaine est invalide.

3-8 Guide du développeur


Gestion des exceptions

En utilisant des exceptions, vous pouvez exprimer la "forme normale" de votre


algorithme puis, après, définir les cas exceptionnels pour lesquels elle n’est pas
applicable. Sans les exceptions, vous devez effectuer un test à chaque fois pour
vous assurer que vous avez bien le droit d’effectuer l’étape suivante du calcul.

Utilisation de l’instance d’exception


La plupart du temps, un gestionnaire d’exception n’a pas besoin d’informations
sur l’exception autre que son type, les instructions qui suivent on..do sont donc
seulement spécifiques au type de l’exception. Néanmoins, dans certains cas vous
avez besoin des informations contenues dans l’instance d’exception.
Pour lire dans un gestionnaire d’exception les informations spécifiques à une
instance d’exception, vous devez utiliser une variante particulière de la
construction on..do qui vous donne accès à l’instance d’exception. Cette forme
spéciale nécessite la spécification d’une variable temporaire utilisée pour stocker
l’instance.
Exemple Créez un nouveau projet qui contient une seule fiche, ajoutez-lui une barre de
défilement et un bouton de commande. Double-cliquez sur le bouton et
définissez le gestionnaire d’événement OnClick suivant :
ScrollBar1.Max := ScrollBar1.Min - 1;
Cette ligne déclenche une exception car la valeur maximum de l’étendue d’une
barre de défilement doit être supérieure à la valeur minimum. Le gestionnaire
d’exception par défaut de l’application ouvre une boîte de dialogue contenant le
message de l’objet exception. Vous pouvez redéfinir la gestion de l’exception
dans ce gestionnaire d’événement afin de créer votre propre boîte message
contenant la chaîne de message de l’exception :
try
ScrollBar1.Max := ScrollBar1.Min - 1;
except
on E: EInvalidOperation do
MessageDlg('Ignorer l’’exception: ' + E.Message, mtInformation, [mbOK], 0);
end;
La variable temporaire (ici E) est du type spécifié après le caractère deux points
(EInvalidOperation dans cet exemple). Vous pouvez, si nécessaire, utiliser
l’opérateur as pour transtyper l’exception dans un type plus spécifique.
Remarque Ne détruisez jamais l’objet exception temporaire. La gestion de l’exception détruit
automatiquement l’objet exception. Si vous détruisez l’objet vous-même,
l’application tente à nouveau de détruire l’objet, ce qui génère une violation
d’accès.

Portée des gestionnaires d’exceptions


Il n’est pas nécessaire de spécifier dans chaque bloc des gestionnaires pour
toutes les exceptions imaginables. Vous n’avez besoin, en fait, de spécifier que
les gestionnaires des exceptions que vous voulez gérer d’une manière particulière
dans un bloc donné.

Sujets de programmation généraux 3-9


Gestion des exceptions

Si un bloc ne gère pas une exception spécifique, l’exécution sort de ce bloc et


revient au bloc contenant ce bloc (ou au code qui a appelé ce bloc) ; l’exception
est toujours déclenchée. Ce processus se répète en augmentant la portée jusqu’à
ce que l’exécution atteigne la portée de l’application ou un bloc qui, à un niveau
quelconque, gère l’exception.

Spécification du gestionnaire d’exception par défaut


Vous pouvez définir un seul gestionnaire d’exception par défaut qui gère toutes
les exceptions n’ayant pas de gestionnaire spécifiquement défini. Pour ce faire,
vous devez ajouter une partie else dans la partie except du bloc de gestion des
exceptions :
try
{ instructions }
except
on ESomething do { code de gestion d’exception spécifique };
else { code de gestion d’exception par défaut};
end;
L’ajout d’une gestion par défaut des exceptions dans un bloc garantit que ce bloc
gère, d’une manière ou d’une autre, toutes les exceptions. Cela redéfinit donc
toute gestion effectuée par un bloc conteneur.
Attention Il n’est pas conseillé d’utiliser le gestionnaire d’exception par défaut qui couvre
un domaine trop vaste. La clause else gère toutes les exceptions, y compris celles
dont vous ne connaissez rien. En général, votre code ne doit gérer que les
exceptions que vous savez comment gérer. Si vous voulez à la fois "faire le
ménage" et réserver la gestion des exceptions à du code disposant de davantage
d’informations sur l’exception et la manière de la gérer, utilisez un bloc
try..finally :
try
try
{ instructions }
except
on ESomething do { code de gestion d’exception spécifique };
end;
finally
{ code de nettoyage};
end;
Pour une autre approche de l’amplification de la gestion des exceptions, voir
Redéclenchement de l’exception.

Gestion des classes d’exceptions


Comme les objets exception font partie d’une hiérarchie, vous pouvez spécifier
des gestionnaires pour toute une partie de la hiérarchie en spécifiant un
gestionnaire pour la classe d’exception dont dérive cette partie de la hiérarchie.

3-10 Guide du développeur


Gestion des exceptions

Exemple Le bloc suivant est le squelette d’un exemple gérant toutes les exceptions de
calcul entier de manière spécifique :
try
{ instructions effectuant des opérations de calcul entier }
except
on EIntError do { gestion spéciale des erreurs de calcul entier };
end;
Vous pouvez toujours définir des gestionnaires plus spécifiques pour des
exceptions plus spécifiques. Vous devez juste placer les gestionnaires spécifiques
avant le gestionnaire générique car l’application recherche les gestionnaires dans
leur ordre d’apparition et exécute le premier gestionnaire applicable trouvé. Par
exemple, le bloc suivant définit une gestion spécifique des erreurs d’étendue et
un autre gestionnaire pour toutes les autres erreurs de calcul entier :
try
{ instructions effectuant des opérations de calcul entier }
except
on ERangeError do { gestion des calculs hors étendue };
on EIntError do { gestion des autres erreurs de calcul entier };
end;
Par contre, si le gestionnaire de EIntError est placé avant le gestionnaire de
ERangeError, l’exécution n’atteint jamais le gestionnaire spécifique à ERangeError.

Redéclenchement de l’exception
Parfois, quand vous gérez localement une exception, vous voulez juste étendre la
gestion définie par le bloc conteneur et pas la remplacer. Mais, bien entendu,
quand votre gestionnaire local en a fini avec l’exception, il détruit
automatiquement l’instance d’exception et le gestionnaire du bloc conteneur ne
peut donc pas agir dessus. Vous pouvez néanmoins empêcher le gestionnaire de
détruire l’exception, ce qui laisse au gestionnaire du conteneur l’opportunité d’y
répondre.
Exemple Quand une exception se produit, vous voulez afficher un message à l’utilisateur,
puis laisser faire la gestion standard. Pour ce faire, déclarez un gestionnaire local
de l’exception qui affiche le message puis utilise le mot réservé raise. C’est ce
que l’on appelle redéclencher l’exception, comme le montre le code suivant :
try
{ instructions }
try
{ instructions spéciales }
except
on ESomething do
begin
{ gestions des instructions spéciales }
raise;{ redéclenche l’exception }
end;
end;
except
on ESomething do ...;{ gestion à effectuer dans tous les cas }
end;

Sujets de programmation généraux 3-11


Gestion des exceptions

Si le code de la partie { instructions } déclenche une exception ESomething, seul le


gestionnaire de la partie except extérieure s’exécute. Par contre, si c’est le code
de la partie { instructions spéciales } qui déclenche une exception ESomething, la
gestion définie dans la partie except intérieure est exécutée suivie par celle, plus
générale, de la partie except extérieure.
En redéclenchant des exceptions, vous pouvez facilement définir une gestion
spécifique d’exceptions pour des cas particuliers sans perdre (ou sans dupliquer)
les gestionnaires existants.

Gestion des exceptions des composants


Les composants de Delphi déclenchent des exceptions pour indiquer des
conditions d’erreur. La plupart des exceptions de composant signalent des
erreurs de programmation qui sinon généreraient une erreur d’exécution. La
technique pour gérer les exceptions de composants n’est pas différente de celle
utilisée pour les exceptions RTL.
Exemple Les erreurs d’intervalles dans les propriétés indicées sont une source fréquente
d’erreur dans les composants. Si, par exemple, pour une boîte liste dont la liste
contient trois éléments (0..2), votre application tente d’accéder à l’élément
numéro 3, la boîte liste déclenche une exception “Index hors limite”.
Le gestionnaire d’événement suivant contient un gestionnaire d’exception qui
informe l’utilisateur de l’accès à un indice invalide de la boîte liste :
procedure TForm1.Button1Click(Sender: TObject);
begin
ListBox1.Items.Add('une chaîne');{ ajoute une chaîne à la boîte liste }
ListBox1.Items.Add('une autre chaîne');{ ajoute une autre chaîne... }
ListBox1.Items.Add('encore une autre chaîne');{ ...et une troisième chaîne}
try
Caption := ListBox1.Items[3];{ Affecte la quatrième chaîne de la boîte liste à
l’intitulé de la fiche }
except
on EStringListError do
MessageDlg('La boîte liste contient moins de quatre chaînes', mtWarning, [mbOK], 0);
end;
end;
Si vous cliquez sur le bouton, comme la boîte liste ne contient que trois chaînes,
l’accès à la quatrième chaîne (Items[3]) déclenche une exception. Si vous cliquez
une seconde fois sur le bouton, d’autres chaînes sont ajoutées à la liste et
l’exception n’est donc plus déclenchée.

3-12 Guide du développeur


Gestion des exceptions

Utilisation de TApplication.HandleException
HandleException propose une gestion par défaut des exceptions au niveau de
l’application. Si une exception passe au travers de tous les blocs try du code de
l’application, l’application appelle automatiquement la méthode HandleException
qui affiche une boîte de dialogue indiquant qu’une erreur a eu lieu. Vous pouvez
utiliser HandleException de la manière suivante :
try
{ instructions }
except
Application.HandleException(Self);
end;
Pour toutes les exceptions sauf EAbort, HandleException appelle, s’il existe, le
gestionnaire d’événement OnException. Si vous voulez à la fois gérer l’exception
et proposer, comme la VCL, ce comportement par défaut, ajoutez un appel de
HandleException à votre code :
try
{ instructions spéciales }
except
on ESomething do
begin
{ ne gère que les instructions spéciales }
Application.HandleException(Self);{ appelle HandleException }
end;
end;
Pour plus d’informations, recherchez routines de gestion des exceptions dans l’index
de l’aide.

Exceptions silencieuses
Les applications Delphi gèrent la plupart des exceptions qui ne sont pas gérées
spécifiquement dans votre code en affichant une boîte de message qui affiche la
chaîne de message de l’objet exception. Vous pouvez également définir des
exceptions “silencieuses” pour lesquelles, par défaut l’application n’affiche pas le
message d’erreur.
Les exceptions silencieuses sont utiles quand vous ne voulez pas gérer une
exception mais simplement abandonner l’opération. L’abandon d’une opération
est semblable à l’utilisation des procédures Break et Exit pour sortir d’un bloc,
mais elle permet de sortir de plusieurs niveaux de blocs imbriqués.
Les exceptions silencieuses descendent toutes du type d’exception standard
EAbort. Le gestionnaire d’exception par défaut des applications VCL Delphi
affiche la boîte de dialogue de message d’erreur pour toutes les exceptions qu’il
reçoit sauf pour celles qui dérivent de EAbort.
Remarque Dans les applications console, la boîte de dialogue d’erreur est affichée même
pour une exception EAbort.

Sujets de programmation généraux 3-13


Gestion des exceptions

Il y a un moyen rapide de déclencher des exceptions silencieuses : au lieu de


construire l’objet manuellement, vous pouvez appeler la procédure Abort. Abort
déclenche automatiquement une exception EAbort qui sort de l’opération en
cours sans afficher de message d’erreur.
Exemple L’exemple suivant propose un exemple simple d’abandon d’une opération. Dans
une fiche contenant une boîte liste vide et un bouton, attachez le code suivant à
l’événement OnClick du bouton :
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
begin
for I := 1 to 10 do{ boucler dix fois }
begin
ListBox1.Items.Add(IntToStr(I));{ ajouter un nombre à la liste }
if I = 7 then Abort;{ arrêter au septième }
end;
end;

Définition d’exceptions personnalisées


Non seulement les exceptions vous permettent de protéger votre code des
erreurs générées par la bibliothèque d’exécution ou par les composants, mais
vous pouvez également utiliser le même mécanisme pour gérer des conditions
exceptionnelles dans votre propre code.
Pour utiliser des exceptions dans votre code, vous devez passer par les étapes
suivantes :
• Déclaration d’un type objet exception
• Déclenchement d’une exception

Déclaration d’un type objet exception


Comme les exceptions sont des objets, la définition d’une nouvelle sorte d’exception
est aussi simple que la déclaration d’un nouveau type d’objet. Bien qu’il soit
possible de déclencher comme exception toute instance d’objet, les gestionnaires
d’exceptions standard ne gèrent que les exceptions dérivées de Exception.
Il est donc préférable de dériver tout nouveau type d’exception de Exception ou de
l’une des autres exceptions standard. De cette manière, si vous déclenchez votre
nouvelle exception dans un bloc de code qui n’est pas protégé par un gestionnaire
spécifique à cette exception, l’un des gestionnaires standard la gérera.
Exemple Par exemple, examinez la déclaration suivante :
type
EMyException = class(Exception);
Si vous déclenchez EMyException sans spécifier de gestionnaire spécifique pour
EMyException, un gestionnaire de Exception (ou un gestionnaire d’exception par
défaut) pourra la gérer. Comme la gestion standard pour Exception affiche le
nom de l’exception déclenchée, vous pourrez au moins savoir que c’est votre
nouvelle exception qui a été déclenchée.

3-14 Guide du développeur


Utilisation des interfaces

Déclenchement d’une exception


Pour indiquer une condition d’erreur dans une application, vous pouvez
déclencher une exception, ce qui implique la construction d’une instance de ce
type et l’appel du mot réservé raise.
Pour déclencher une exception, appelez le mot réservé raise en le faisant suivre
par une instance d’un objet exception. Quand un gestionnaire d’exception gère
effectivement l’exception, il se termine en détruisant l’instance d’exception : vous
n’avez donc jamais à le faire vous-même.
L’initialisation de l’adresse de l’exception se fait par l’intermédiaire de la variable
ErrorAddr définie dans l’unité System. Le déclenchement d’une exception affecte à
cette variable l’adresse à laquelle l’application déclenche l’exception. Vous
pouvez faire référence à ErrorAddr dans vos gestionnaires d’exceptions, par
exemple pour informer l’utilisateur de l’emplacement de l’erreur. Vous pouvez
aussi spécifier la valeur de ErrorAddr quand vous déclenchez une exception.
Pour spécifier l’adresse de l’erreur d’une exception, ajoutez le mot réservé at
après l’instance d’exception en la faisant suivre d’une expression adresse, par
exemple un identificateur.
Par exemple, étant donné la déclaration suivante :
type
EPasswordInvalid = class(Exception);
vous pouvez déclencher une exception “mot de passe incorrect” à tout moment
en appelant raise avec une instance de EPasswordInvalid, comme suit :
if Password <> CorrectPassword then
raise EPasswordInvalid.Create('Mot de passe saisi incorrect');

Utilisation des interfaces


Le mot réservé interface de Delphi vous permet de créer et d’utiliser des
interfaces dans votre application. Les interfaces constituent un moyen d’étendre
le modèle d’héritage simple de la VCL en permettant à une même classe
d’implémenter plusieurs interfaces et à plusieurs classes n’ayant pas le même
ancêtre de partager la même interface. Les interfaces sont utiles quand le même
type d’opérations, par exemple la manipulation de flux, portent sur une gamme
variée d’objets. Les interfaces constituent également un aspect fondamental des
modèles d’objets distribués COM et CORBA.

Création d’interfaces
Une interface est semblable à une classe ne contenant que des méthodes
abstraites et une définition claire de ses fonctionnalités. Strictement parlant, les
définitions des méthodes d’interface spécifient le nombre et le type de leurs
paramètres, le type renvoyé et le comportement prévu. Les méthodes d’une
interface sont reliées, sémantiquement ou logiquement, pour indiquer le rôle de

Sujets de programmation généraux 3-15


Utilisation des interfaces

l’interface. Par convention, les interfaces sont nommées en fonction de leur


comportement en préfixant leur nom par une lettre I en majuscule. Par exemple,
une interface IMalloc doit allouer, libérer et gérer de la mémoire. De même, une
interface IPersist peut être utilisée comme une interface de base générale pour
des descendants, chacun d’eux définissant des prototypes de méthode spécifiques
permettant de charger et d’enregistrer l’état d’un objet dans un stockage, un flux
ou un fichier. Voici un exemple simple de déclaration d’une interface :
type
IEdit = interface
procedure Copy; stdcall ;
procedure Cut; stdcall;
procedure Paste; stdcall;
function Undo: Boolean; stdcall;
end;
Comme les classes abstraites (abstract), les interfaces ne sont jamais instanciées
elles-mêmes. Pour utiliser une interface, vous devez l’obtenir en l’implémentant
dans une classe.
Pour implémenter une interface, vous devez définir une classe qui déclare
l’interface dans sa liste d’ancêtres, ce qui indique qu’elle implémente toutes les
méthodes de l’interface :
TEditor = class(TInterfacedObject, IEdit)
procedure Copy; stdcall ;
procedure Cut; stdcall;
procedure Paste; stdcall;
function Undo: Boolean; stdcall;
end;
Alors que les interfaces définissent le comportement et la signature de leurs
méthodes, elles n’en définissent pas l’implémentation. Dès lors que
l’implémentation faite dans la classe se conforme à la définition de l’interface,
l’interface est totalement polymorphique : l’accès et l’utilisation de l’interface
restent identiques dans toutes ses implémentations.

Partage d’interfaces entre des classes


L’utilisation d’interfaces permet d’envisager une conception qui sépare la
manière d’utiliser une classe de la manière dont elle est implémentée. Deux
classes peuvent partager la même interface sans descendre nécessairement de la
même classe de base. Cet appel polymorphique de la même méthode pour des
objets sans rapports entre eux est possible dans la mesure où les objets
implémentent la même interface. Par exemple, soit l’interface :
IPaint = interface
procedure Paint;
end;
et les deux classes suivantes :
TSquare = class(TPolygonObject, IPaint)
procedure Paint;
end;

3-16 Guide du développeur


Utilisation des interfaces

TCircle = class(TCustomShape, IPaint)


procedure Paint;
end;
Que ces deux classes aient ou non un ancêtre commun, elles sont toujours
compatibles pour l’affectation avec une variable de type IPaint :
var
Painter: IPaint;
begin
Painter := TSquare.Create;
Painter.Paint;
Painter := TCircle.Create;
Painter.Paint;
end;
Il est possible d’obtenir le même résultat en faisant dériver TCircle et TSquare
d’une classe TFigure qui implémente la méthode virtuelle Paint. Dans ce cas
TCircle et TSquare doivent surcharger la méthode Paint. IPaint est alors remplacée
par TFigure. Cependant, considérez l’interface suivante :
IRotate = interface
procedure Rotate(Degrees: Integer);
end;
qui a du sens pour un rectangle, mais pas pour le cercle. Les classes seraient
alors définies de la manière suivante :
TSquare = class(TRectangularObject, IPaint, IRotate)
procedure Paint;
procedure Rotate(Degrees: Integer);
end;
TCircle = class(TCustomShape, IPaint)
procedure Paint;
end;
Vous pouvez, ultérieurement créer une classe TFilledCircle qui implémente
l’interface IRotate afin de permettre la rotation du motif utilisé pour remplir le
cercle sans avoir à ajouter la rotation au cercle simple.
Remarque Dans ces exemples, on suppose que la classe de base immédiate ou une classe
ancêtre a implémenté les méthodes de IUnknown qui gèrent le comptage de
références. Pour plus d’informations, voir “Implémentation de IUnknown” à la
page 3-18 et “Gestion mémoire des objets interface” à la page 3-22.

Utilisation d’interfaces avec des procédures


Les interfaces permettent également d’écrire des procédures génériques pouvant
gérer des objets sans que ces objets descendent d’une classe de base particulière.
En utilisant les interfaces IPaint et IRotate définies précédemment, vous pouvez
écrire les procédures suivantes :
procedure PaintObjects(Painters: array of IPaint);
var
I: Integer;

Sujets de programmation généraux 3-17


Utilisation des interfaces

begin
for I := Low(Painters) to High(Painters) do
Painters[I].Paint;
end;
procedure RotateObjects(Degrees: Integer; Rotaters: array of IRotate);
var
I: Integer;
begin
for I := Low(Rotaters) to High(Rotaters) do
Rotaters[I].Rotate(Degrees);
end;
RotateObjects n’a pas besoin que les objets sachent se dessiner par eux-mêmes et
PaintObjects n’exige pas que les objets sachent comment pivoter. Cela permet aux
objets définis précédemment d’être utilisés plus fréquemment que s’ils n’avaient
été écrits que pour la classe TFigure.
Pour des détails sur la syntaxe, la définition du langage et les règles s’appliquant
aux interfaces, voir dans le Guide du langage Pascal Objet, la section Interfaces
d’objet.

Implémentation de IUnknown
Toutes les interfaces dérivent, directement ou non, de l’interface IUnknown. Cette
interface définit les fonctionnalités essentielles d’une interface, c’est-à-dire
l’interrogation dynamique et la gestion de la durée de vie. Ces fonctionnalités
sont mises en place par les trois méthodes de IUnknown :
• QueryInterface est une méthode d’interrogation dynamique d’un objet donné
qui obtient les références des interfaces gérées par l’objet.
• AddRef est une méthode de comptage de références qui incrémente le
compteur à chaque appel réussi de QueryInterface. Tant que le compteur de
référence est non nul, l’objet doit rester en mémoire.
• Release est utilisée en conjonction avec AddRef pour permettre à un objet de
connaître sa durée de vie et de déterminer s’il peut se supprimer lui-même.
Quand le compteur de références atteint zéro, l’implémentation de l’interface
libère les objets sous-jacents.
Chaque classe qui implémente des interfaces doit implémenter les trois méthodes
de IUnknown, les autres méthodes déclarées dans toutes ses interfaces ancêtre,
ainsi que toutes les méthodes déclarées dans l’interface même. Néanmoins, vous
pouvez hériter de l’implémentation des méthodes d’interface déclarées dans
votre classe.

3-18 Guide du développeur


Utilisation des interfaces

TInterfacedObject
La VCL définit une classe simple, TInterfacedObject qui, pratiquement, sert de
classe de base car elle implémente les méthodes de IUnknown. La classe
TInterfacedObject est déclarée de la manière suivante dans l’unité System :
type
TInterfacedObject = class(TObject, IUnknown)
private
FRefCount: Integer;
protected
function QueryInterface(const IID: TGUID; out Obj): Integer; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public
property RefCount: Integer read FRefCount;
end;
Dériver directement de TInterfacedObject est trivial. Dans l’exemple de déclaration
suivant, TDerived est un descendant direct de TInterfacedObject qui implémente
une interface hypothétique, IPaint :
type
TDerived = class(TInterfacedObject, IPaint)
...
end;
Comme TInterfacedObject implémente les méthodes de IUnknown, elle gère
automatiquement le comptage de références et la gestion mémoire pour les objets
interfacés. Pour davantage d’informations, voir “Gestion mémoire des objets
interface” à la page 3-22 qui traite également de l’écriture de classes
implémentant des interfaces sans utiliser le mécanisme de comptage de
références inhérent à TInterfacedObject.

Utilisation de l’opérateur as
Les classes implémentant des interfaces peuvent utiliser l’opérateur as pour se
lier dynamiquement à l’interface. Dans l’exemple suivant :
procedure PaintObjects(P: TInterfacedObject)
var
X: IPaint;
begin
X := P as IPaint;
{ instructions }
end;
la variable P, de type TInterfacedObject, peut être affectée à la variable X qui est
une référence à l’interface IPaint. La liaison dynamique rend cette affectation
possible. Le compilateur génère pour cette affectation le code appelant la
méthode QueryInterface de l’interface IUnknown de P ; le compilateur ne peut
savoir à partir du type déclaré de P si l’instance de P gère réellement IPaint. A

Sujets de programmation généraux 3-19


Utilisation des interfaces

l’exécution, soit P se résoud en une référence à une interface IPaint, soit une
exception est déclenchée. Dans l’un ou l’autre cas, l’affectation de P à X ne
génère pas une erreur à la compilation comme se serait le cas si P avait pour
type une classe n’implémentant pas IUnknown.
Quand vous utilisez l’opérateur as pour une liaison dynamique avec une
interface, vous devez respecter les règles suivantes :
• Déclaration explicite de IUnknown : même si toutes les interfaces dérivent de
IUnknown, il ne suffit pas qu’une classe implémente les méthodes de
IUnknown pour pouvoir utiliser l’opérateur as. Cela reste vrai même si la classe
implémente également les interfaces qu’elle déclare explicitement. La classe
doit déclarer explicitement IUnknown dans sa liste d’ancêtres.
• Utilisation d’un IID : les interfaces peuvent utiliser un identificateur basé sur
un identificateur global unique (GUID). Les GUID utilisés pour identifier des
interfaces sont appelés des identificateurs d’interface (IID). Si vous utilisez
l’opérateur as avec une interface, elle doit avoir un IID associé. Pour créer un
nouveau GUID dans votre code source, vous pouvez utiliser le raccourci
Ctrl+Shift+G de l’éditeur de code.

Réutilisation de code et délégation


Une des manières pour réutiliser du code avec les interfaces consiste à utiliser
un objet contenant un autre objet ou un objet contenu dans un autre objet. La
VCL utilise les propriétés de type objet comme un moyen de contenir et de
réutiliser du code. Pour exploiter cette caractéristique des interfaces, Delphi
emploie le mot clé implements qui rend facile l’écriture de code pour déléguer
tout ou partie de l’implémentation d’une interface à un sous-objet. L’agrégation
est un autre moyen de réutiliser du code grâce à des conteneurs et à la
délégation. Dans l’agrégation, un objet externe contient un objet interne qui
implémente des interfaces qui ne sont exposées que par l’objet extérieur. La VCL
propose des classes qui gèrent l’agrégation.

Utilisation de implements pour la délégation


De nombreuses classes de la VCL ont des propriétés qui sont des sous-objets.
Vous pouvez également utiliser des interfaces comme type d’une propriété.
Quand une propriété est de type interface (ou d’un type de classe qui
implémente les méthodes d’une interface), vous pouvez utiliser le mot clé
implements pour spécifier que les méthodes de cette interface sont déléguées à
la référence d’objet ou d’interface qui est l’instance de la propriété. Le délégué
doit seulement fournir l’implémentation des méthodes : il n’a pas besoin de
déclarer qu’il gère l’interface. La classe contenant la propriété doit inclure
l’interface dans sa liste d’ancêtres. Par défaut, l’utilisation du mot-clé implements
délègue tous les méthodes d’interface. Pour redéfinir le comportement par
défaut, vous pouvez néanmoins utiliser les clauses de résolution des méthodes
ou déclarer des méthodes dans votre classe qui implémentent certaines des
méthodes de l’interface.

3-20 Guide du développeur


Utilisation des interfaces

L’exemple suivant utilise le mot-clé implements dans la conception d’un objet


adaptateur de couleur qui convertit une valeur de couleur 8 bits RVB en une
référence Color:
unit cadapt;
type
IRGB8bit = interface
['{1d76360a-f4f5-11d1-87d4-00c04fb17199}']
function Red: Byte;
function Green: Byte;
function Blue: Byte;
end;
IColorRef = interface
['{1d76360b-f4f5-11d1-87d4-00c04fb17199}']
function Color: Integer;
end;
{ TRGB8ColorRefAdapter associe un IRGB8bit à un IColorRef }
TRGB8ColorRefAdapter = class(TInterfacedObject, IRGB8bit, IColorRef)
private
FRGB8bit: IRGB8bit;
FPalRelative: Boolean;
public
constructor Create(rgb: IRGB8bit);
property RGB8Intf: IRGB8bit read FRGB8bit implements IRGB8bit;
property PalRelative: Boolean read FPalRelative write FPalRelative;
function Color: Integer;
end;
implementation
constructor TRGB8ColorRefAdapter.Create(rgb: IRGB8bit);
begin
FRGB8bit := rgb;
end;
function TRGB8ColorRefAdapter.Color: Integer;
begin
if FPalRelative then
Result := PaletteRGB(RGB8Intf.Red, RGB8Intf.Green, RGB8Intf.Blue)
else
Result := RGB(RGB8Intf.Red, RGB8Intf.Green, RGB8Intf.Blue);
end;
end.
Pour davantage d’informations sur la syntaxe, les détails de l’implémentation et
les règles concernant le mot-clé implements, voir dans le Guide du Langage Pascal
Objet, le chapitre Interfaces d’objet.

Agrégation
L’agrégation propose une approche modulaire de la réutilisation de code via des
sous-objets définissant les fonctionnalités d’un objet conteneur tout en lui cachant
les détails de l’implémentation. Dans l’agrégation, un objet externe implémente
une ou plusieurs interfaces. La seule exigence est qu’il implémente IUnknown.

Sujets de programmation généraux 3-21


Utilisation des interfaces

L’objet interne (il peut y en avoir plusieurs) peut implémenter une ou plusieurs
interfaces, mais seul l’objet externe expose les interfaces. Cela vaut pour les
interfaces qu’il implémente et pour celles implémentées par les objets qu’il
contient. Les clients ne savent rien des objets internes. C’est l’objet externe qui
donne accès aux interfaces de l’objet interne, leur implémentation étant
totalement transparente. Cependant la classe objet externe peut échanger le type
de classe de l’objet interne avec toute classe qui implémente la même interface.
Réciproquement, le code des classes d’objet interne peut être partagé avec les
autres classes souhaitant l’utiliser.
Le modèle d’implémentation de l’agrégation définit des règles explicites pour
l’implémentation de IUnknown en utilisant la délégation. L’objet interne doit
implémenter une interface IUnknown sur lui-même qui contrôle le comptage de
références pour l’objet interne. Cette implémentation de IUnknown surveille la
relation entre les objets interne et externe. Par exemple, quand un objet de son
type (l’objet interne) est créé, la création réussit uniquement pour une interface
demandée de type IUnknown. L’objet interne implémente également une
deuxième interface IUnknown pour toutes les interfaces qu’il implémente. Ces
sont les interfaces exposées par l’objet externe. Ce deuxième IUnknown délègue
tous les appels de QueryInterface, AddRef et Release à l’objet externe.
Reportez-vous à la documentation en ligne Microsoft pour connaître les règles
s’appliquant lors de la création d’une agrégation. Quand vous écrivez vos
propres classes d’agrégation, vous pouvez également consulter les détails de
l’implémentation de IUnknown dans TComObject. TComObject est une classe COM
qui gère l’agrégation. Si vous écrivez des applications COM, vous pouvez
également utiliser directement TComObject comme classe de base.

Gestion mémoire des objets interface


L’un des concepts clé de la conception d’interfaces est la gestion de la durée de
vie des objets qui les implémentent. Les méthodes AddRef et Release de IUnknown
constituent un moyen d’implémenter cette fonctionnalité. La définition de leur
comportement spécifie qu’elles surveillent la durée de vie d’un objet en
incrémentant le compteur de références à l’objet quand une référence d’interface
est transmise à un client et qu’elles détruisent l’objet quand le compteur de
référence est nul.
Si vous créez des objets COM pour des applications distribuées, vous devez
respecter strictement les règles de comptage de références. Par contre, si vous
n’utilisez les interfaces que de manière interne à l’application, vous pouvez
décider ou non de vous y conformer selon la nature de votre objet et comment
vous comptez l’utiliser.

Utilisation du comptage de références


Delphi vous fournit l’essentiel de la gestion mémoire IUnknown grâce à son
implémentation de l’interrogation et du comptage de références de l’interface.
Cependant, si vous utilisez un objet qui vit et meurt via ses interfaces, vous
pouvez aisément utiliser le comptage de références en dérivant de ces classes.

3-22 Guide du développeur


Utilisation des interfaces

TInterfacedObject est une non-CoClasse qui définit ce comportement. Si vous


choisissez d’utiliser le comptage de références, vous devez faire attention à ne
manipuler l’objet que sous la forme d’une référence d’interface et d’être cohérent
dans votre comptage de références. Par exemple :
procedure beep(x: ITest);
function test_func()
var
y: ITest;
begin
y := TTest.Create; // comme y est de type ITest, le compteur de références vaut un
beep(y); // l’appel de la fonction beep incrémente le compteur de références
// et le décrémente à son retour
y.something; // l’objet est toujours là avec un compteur de références valant un
end;
C’est la manière la plus claire et la plus prudente de gérer la mémoire et, si vous
utilisez TInterfacedObject, elle est utilisée automatiquement. Si vous ne respectez
pas ces règles, votre objet peut disparaître inopinément, comme l’illustre le code
suivant :
function test_func()
var
x: TTest;
begin
x := TTest.Create; // pas encore de compteur de références pour l’objet
beep(x as ITest); // le compteur est incrémenté par l’appel de beep
// et décrémenté à son retour
x.something; // surprise! l’objet n’est plus là
end;
Remarque Dans les exemples précédents, la procédure beep, telle qu’elle est déclarée,
incrémente le compteur de référence (appel de AddRef) pour le paramètre. Par
contre, les déclarations suivantes :
procedure beep(const x: ITest);
ou
procedure beep(var x: ITest);
ne le font pas. Ces déclarations génèrent un code plus concis et plus rapide.
Vous ne pouvez pas utiliser le comptage de références dans un cas : si votre
objet est un composant ou un contrôle contenu dans un autre composant. Dans
un tel cas, le comptage de références ne peut être appliqué de manière
cohérente : vous pouvez toujours utiliser les interfaces, mais sans utiliser le
comptage de référence car la durée de vie de l’objet n’est pas gouvernée par ses
interfaces.

Sujets de programmation généraux 3-23


Utilisation des interfaces

Situations où il ne faut pas utiliser le comptage de références


Si votre objet est un composant ou un contrôle de la VCL détenu par un autre
composant, votre objet utilise alors un autre système de gestion mémoire ayant
son origine dans TComponent. Vous ne devez pas mélanger l’approche de la
durée de vie des objets utilisée par les composants VCL avec le système COM de
comptage de références. Si vous voulez créer un composant qui gère les
interfaces, vous pouvez définir une implémentation vide des méthodes AddRef et
Release de IUnknown afin de court-circuiter le mécanisme COM de comptage de
références. Par exemple :
function TMyObject.AddRef: Integer;
begin
Result := -1;
end;
function TMyObject.Release: Integer;
begin
Result := -1;
end;
Vous devez quand même implémenter normalement QueryInterface afin de
permettre l’interrogation dynamique de votre objet.
Comme vous implémentez QueryInterface, vous pouvez toujours utiliser
l’opérateur as pour des interfaces de composant dans la mesure où vous créez
un identificateur d’interface (IID). Vous pouvez également utiliser l’agrégation. Si
l’objet externe est un composant, l’objet interne implémente normalement le
comptage de références en déléguant au “controlling Unknown”. C’est au niveau
de l’objet composant externe que vous pouvez choisir de court-circuiter les
méthodes AddRef et Release afin de gérer la mémoire en utilisant l’approche VCL.
En fait, vous pouvez utiliser TInterfacedObject comme classe de base d’un objet
interne pour une agrégation utilisant un composant comme objet externe.
Remarque Le “controlling Unknown” est l’interface IUnknown implémentée par l’objet
externe et pour laquelle le comptage de références de l’objet est tenu à jour. Pour
davantage d’informations sur les différences entre l’implémentation de l’interface
IUnknown par les objets interne et externe, voir “Agrégation” à la page 3-21 ainsi
que les rubriques d’aide Microsoft traitant de “controlling Unknown”.

Utilisation d’interfaces dans des applications


distribuées
Les interfaces sont un élément fondamental dans les modèles d’objets distribués
COM et CORBA. Delphi gère ces technologies via des classes de base qui
étendent les fonctionnalités de base d’une interface définies dans TInterfacedObject
(qui n’implémente que les méthodes de l’interface IUnknown).
Les classes COM ajoutent des fonctionnalités permettant d’utiliser des fabricants
de classe et des identificateurs de classe (CLSID). Les fabricants de classes
permettent de créer des instances de classe à partir de CLSID. Les identificateurs
de classe (CLSID) sont utilisés pour recenser et manipuler des classes COM. Les

3-24 Guide du développeur


Utilisation des chaînes

classes COM qui disposent de fabricants de classe et d’identificateurs de classe


sont appelées des CoClasses. Les CoClasses exploitent la gestion des versions de
QueryInterface de telle façon que si un module logiciel est actualisé, il est possible
d’appeler QueryInterface à l’exécution pour interroger l’objet afin de connaître ses
fonctions actuelles.
Il est ainsi possible pour un client d’utiliser immédiatement les nouvelles
fonctions d’anciennes interfaces ou les nouvelles interfaces ou caractéristiques
d’un objet. En même temps, les objets restent compatibles avec le code existant
des clients : il n’est pas nécessaire de recompiler les clients car l’implémentation
des interfaces est cachée (les méthodes et leurs paramètres restant inchangés).
Dans les applications COM, le développeur peut modifier l’implémentation afin
d’améliorer les performances, ou pour d’autres motifs, sans perturber le code
client qui dépend de cette interface. Pour davantage d’informations sur les
interfaces COM, voir chapitre 44, “Présentation des technologies COM.”.
CORBA est une autre technologie d’application utilisant des objets distribués.
L’utilisation des interfaces dans les applications CORBA se fait par
l’intermédiaire de classes stub pour le client et de classes squelettes pour le
serveur. Ces classes stub et squelettes gèrent les détails du marshaling des appels
d’interface afin que les valeurs des paramètres et les valeurs renvoyées soient
correctement transmises. Les applications doivent utiliser une classe stub ou
squelette, ou employer l’interface d’appel dynamique (abrégé en anglais par DII)
qui convertit tous les paramètres en variants spéciaux afin qu’ils contiennent
eux-même leurs propres informations de type.
Bien que cela ne soit pas une caractéristique impérative dans la technologie CORBA,
Delphi implémente CORBA en utilisant des fabricants de classe, de la même manière
que COM utilise les fabricants de classe et les CoClasses. En unifiant ainsi ces deux
architectures distribuées, Delphi peut gérer un serveur combiné COM/CORBA
capable de répondre simultanément à des clients COM ou CORBA. Pour davantage
d’informations sur l’utilisation des interfaces dans Corba, voir chapitre 28, “Ecriture
d’applications CORBA.”

Utilisation des chaînes


Delphi dispose de divers types caractère ou chaîne qui sont apparus au fur et à
mesure de l’évolution du langage Pascal Objet. Cette section offre un aperçu de
ces types, de leur rôle et de leurs utilisations. Pour des détails sur la syntaxe du
langage, voir dans l’aide en ligne du langage Pascal objet Types de chaînes

Types caractère
Delphi a trois types caractère : Char, AnsiChar et WideChar.
Le type caractère Char provient du Pascal standard, il a été utilisé dans Turbo
Pascal puis en Pascal Objet. Plus tard, le Pascal Objet a ajouté les types AnsiChar
et WideChar comme des types caractère spécifiques utilisés pour gérer les
représentations standard des caractères dans le système d’exploitation Windows.

Sujets de programmation généraux 3-25


Utilisation des chaînes

AnsiChar a été ajouté pour gérer le jeu de caractères ANSI sur 8 bits et WideChar
pour gérer le jeu de caractères 16 bits Unicode. Les caractères de type WideChar
sont également appelés caractères étendus. Les caractères étendus sont codés sur
deux octets afin que le jeu de caractères puisse représenter davantage de
caractères différents. Quand AnsiChar et WideChar ont été implémentés, Char est
devenu le type caractère par défaut représentant l’implémentation dont
l’utilisation est conseillée pour un système donné. Si vous utilisez Char dans une
application, n’oubliez pas que son implémentation est susceptible de changer des
versions ultérieures de Delphi.
Le tableau suivant décrit brièvement ces types caractère :

Tableau 3.2 Types caractère du Pascal Objet


Type Octets Contenu Rôle
Char 1 Contient un seul caractère Type de caractère par défaut.
ANSI.
AnsiChar 1 Contient un seul caractère Caractère ANSI 8 bits standard sur
ANSI. Windows.
WideChar 2 Contient un seul caractère Caractère Unicode 16 bit standard sur
Unicode. Windows.

Pour davantage d’informations sur l’utilisation de ces types caractère, voir dans
l’aide en ligne du Guide du langage Pascal Objet la rubrique Types caractère
Pour davantage d’informations sur les caractères Unicode, voir dans l’aide en
ligne du Guide du langage Pascal Objet la rubrique A propos des jeux de
caractères étendus

Types chaîne
Delphi propose trois catégories de types que vous pouvez utiliser pour
manipuler des chaînes : les pointeurs de caractère, les types chaîne et les classes
chaîne VCL. Cette section présente les types chaîne et décrit leur utilisation avec
les pointeurs de caractère. Pour des informations sur les classes chaîne VCL, voir
dans l’aide en ligne la rubrique TStrings.
Delphi dispose aujourd’hui de trois implémentations de chaîne : les chaînes
courtes, les chaînes longues et les chaînes étendues. Il existe plusieurs types
chaîne qui représentent ces implémentations. De plus, le mot réservé string
correspond par défaut à l’implémentation de chaîne actuellement recommandée.

Chaînes courtes
String a été le premier type chaîne utilisé en Turbo Pascal. String était à
l’origine implémenté avec une chaîne courte. Les chaînes courtes allouent entre 1
et 256 octets : le premier octet contenant la longueur de la chaîne, les octets
restants contenant les caractères de la chaîne :
S: string [0..n]// le type string original

3-26 Guide du développeur


Utilisation des chaînes

Quand les chaînes longues ont été implémentées, string a été modifié pour
exploiter par défaut une implémentation de chaîne longue et ShortString a été
introduit comme type permettant la compatibilité ascendante. ShortString est un
type prédéfini pour une chaîne de longueur maximum :
S: string [255]// Le type ShortString
La quantité de mémoire allouée pour un ShortString est statique, c’est-à-dire
qu’elle est déterminée à la compilation. Par contre, l’emplacement de la mémoire
d’un ShortString peut être allouée dynamiquement (par exemple si vous utilisez
un PShortString qui est un pointeur sur un ShortString). Le nombre d’octets de
stockage employés par une variable de type chaîne courte correspond à la
longueur maximum du type chaîne courte plus un. Pour le type prédéfini
ShortString, la taille est donc de 256 octets.
Les chaînes courtes, déclarées en utilisant la syntaxe string[0..n] et le type
prédéfini ShortString existent essentiellement pour proposer une compatibilité
ascendante avec les versions précédentes de Delphi et de Borland Pascal.
Une directive de compilation, $H, détermine si le mot réservé string correspond
à une chaîne longue ou courte. A l’état par défaut, {$H+}, string représente une
chaîne longue. Vous pouvez le changer en un ShortString en utilisant la directive
{$H-}. L’état {$H-} est surtout pratique pour utiliser du code écrit pour des
versions précédentes du Pascal Objet qui utilisaient par défaut le type chaîne
courte. Toutefois, les chaînes courtes peuvent s’avérer utiles dans des structures
de données quand vous avez besoin de composants de taille fixe ou dans des
DLL si vous ne souhaitez pas utiliser l’unité ShareMem (voir aussi dans l’aide en
ligne Gestion de la mémoire). Vous pouvez redéfinir localement la signification
des définitions de type chaîne pour générer des chaînes courtes. Vous pouvez
aussi changer les déclarations de chaînes courtes en string[255] ou en ShortString
qui sont dépourvues d’ambiguïtés et indépendantes de la directive $H.
Pour davantage d’informations sur les chaînes et le type ShortString, voir dans
l’aide en ligne du Guide du langage Pascal Objet la rubrique Chaînes courtes

Chaînes longues
Les chaînes longues sont des chaînes allouées dynamiquement dont la longueur
maximum est limitée uniquement par la mémoire disponible. Comme les chaînes
courtes, les chaînes longues utilisent des caractères ANSI sur 8 bits et un
indicateur de longueur. A la différence des chaînes courtes, les chaînes longues
n’ont pas un élément zéro contenant la longueur dynamique de la chaîne. Pour
connaître la longueur d’une chaîne longue, vous devez utiliser la fonction
standard Length et pour spécifier sa longueur vous devez utiliser la procédure
standard SetLength. Les chaînes longues utilisent le comptage de références et,
comme les PChars, ce sont des chaînes à zéro terminal. Pour davantage
d’informations sur l’implémentation des chaînes longues, voir dans l’aide en
ligne du Guide du langage Pascal Objet la rubrique Chaînes longues

Sujets de programmation généraux 3-27


Utilisation des chaînes

Les chaînes longues sont désignées par le mot réservé string et par
l’identificateur prédéfini AnsiString. Dans les nouvelles applications, il est
conseillé d’utiliser le type chaîne longue. Tous les composants de la VCL sont
compilés de cette manière, généralement en utilisant string. Si vous écrivez des
composants, ils doivent également utiliser des chaînes longues tout comme doit
le faire le code recevant des données provenant de propriétés VCL de type
chaîne. Si vous voulez écrire du code spécifique qui utilise systématiquement
une chaîne longue, vous devez utiliser AnsiString. Si vous voulez écrire du code
flexible qui vous permet de changer facilement le type quand une nouvelle
implémentation de chaîne deviendra la norme, vous devez alors utiliser string.

Chaînes étendues
Le type WideChar permet de représenter des chaînes de caractères étendus comme
des tableaux de WideChars. Les chaînes étendues sont des chaînes composées de
caractères Unicode sur 16 bits. Comme les chaînes longues, les chaînes étendues
sont allouées dynamiquement et leur longueur maximum n’est limitée que par la
quantité de mémoire disponible. Par contre, les chaînes étendues n’utilisent pas le
comptage de références. La mémoire allouée dynamiquement pour la chaîne est
libérée quand la chaîne étendue sort de portée. Pour tout le reste, les chaînes
étendues ont les mêmes attributs que les chaînes longues. Le type chaîne étendu
est désigné par l’identificateur prédéfini WideString.
Comme la version 32 bits de OLE utilise Unicode, toutes les chaînes doivent être
de type chaîne étendu dans les propriétés et paramètres de méthodes OLE
Automation. De plus, la plupart des fonctions de l’API OLE utilisent des chaînes
étendues à zéro terminal.
Pour davantage d’informations sur les chaînes étendues, voir dans l’aide en ligne
du Guide du langage Pascal Objet la rubrique Chaînes étendues

Types PChar
Un PChar est un pointeur sur une chaîne à zéro terminal de caractères de type
Char. Chacun des trois types caractère dispose d’un type de pointeur prédéfini :
• Un PChar est un pointeur sur une chaîne à zéro terminal de caractères 8 bits.
• Un PAnsiChar est un pointeur sur une chaîne à zéro terminal de caractères 8 bits.
• Un PWideChar est un pointeur sur une chaîne à zéro terminal de caractères
16 bits.
PChar est, avec les chaînes courtes, l’un des types chaîne qui existaient à l’origine
dans le Pascal Objet. Il a été conçu à l’origine pour assurer la compatibilité avec
les types du langage C et de l’API Windows.

3-28 Guide du développeur


Utilisation des chaînes

Chaînes ouvertes
Le type OpenString est obsolète mais vous pouvez le rencontrer dans du code
ancien. Il n’existe que pour la compatibilité 16 bits et n’est autorisé que dans les
paramètres. OpenString était utilisé, avant l’implémentation des chaînes longues,
pour permettre le transfert comme paramètre d’une chaîne courte de taille
indéterminée. Par exemple, la déclaration suivante :
procedure a(v : openstring);
permet de transmettre comme paramètre une chaîne de longueur quelconque. En
son absence, la longueur de chaîne des paramètres formel et réel doivent
correspondre exactement. Vous n’avez pas besoin d’utiliser OpenString dans les
nouvelles applications que vous écrivez.
Pour d’autres informations sur la directive de compilation {$P+/-} voir
“Directives de compilation portant sur les chaînes” à la page 35 de ce chapitre.

Routines de la bibliothèque d’exécution manipulant


des chaînes
La bibliothèque d’exécution propose de nombreuses routines de manipulation
des chaînes spécialisées pour les différents types chaîne. Il y a des routines pour
les chaînes étendues, les chaînes longues et les chaînes à zéro terminal (c’est-à-
dire PChar). Les routines gérant les types PChar utilisent le zéro terminal pour
déterminer la longueur des chaînes. Pour davantage d’informations sur les
chaînes à zéro terminal, voir Utilisation des chaînes à zéro terminal dans l’aide
en ligne du Guide du langage Pascal Objet.
La bibliothèque d’exécution propose également des routines de formatage de
chaîne. Il n’y a pas de catégorie de routines pour les types ShortString.
Néanmoins, certaines routines prédéfinies dans le compilateur gèrent le type
ShortString. C’est, par exemple, le cas des fonctions standard Low et High.
Comme les chaînes longues et étendues sont les plus fréquemment utilisées, les
sections suivantes décrivent les routines les manipulant.

Routines manipulant les caractères étendus


Quand vous manipulez des chaînes dans une application, vous devez vous
assurer que votre code peut gérer les chaînes rencontrées sur les diverses cibles
locales. Il est parfois nécessaire d’utiliser les caractères étendus et les chaînes
étendues. En fait, l’une des manières de gérer les jeux de caractères
idéographiques consiste à convertir tous les caractères vers un schéma de codage
à base de caractères étendus comme Unicode. La bibliothèque d’exécution
propose les fonctions suivantes permettant d’effectuer la conversion entre des
chaînes de caractères sur un ou plusieurs octets et des chaînes Unicode :
• StringToWideChar
• WideCharLenToString
• WideCharLenToStrVar
• WideCharToString
• WideCharToStrVar

Sujets de programmation généraux 3-29


Utilisation des chaînes

L’utilisation d’un schéma de codage avec des caractères étendus présente cet
avantage que vous pouvez avoir sur les chaînes des présupposés qui ne sont pas
valables dans les systèmes MBCS. Il y a en effet une relation directe entre le
nombre d’octets de la chaîne et son nombre de caractères. Il n’y a pas le risque,
comme avec les jeux de caractères MBCS, de couper un caractère en deux ou de
confondre le deuxième octet d’un caractère avec le début d’un autre caractère.
Par contre, les caractères étendus présentent un inconvénient : Windows 95 ne
gère pas l’appel des fonctions de l’API avec des caractères étendus. C’est pour
cela que tous les composants VCL représentent toutes les valeurs chaîne comme
des chaînes sur un octet ou des chaînes sur plusieurs octets (MBCS). Effectuer la
conversion entre le système de caractères étendu et le système MBCS à chaque
fois que vous lisez ou écrivez une propriété chaîne exigerait une quantité
phénoménale de code supplémentaire et ralentirait vos applications. Par contre,
vous pouvez traduire une propriété chaîne en caractères étendus quand vous
voulez mettre en oeuvre des algorithmes de manipulation de chaînes particuliers
qui exploitent la correspondance 1:1 entre les caractères et WideChars.

Routines usuelles de manipulation des chaînes longues


Il est possible de regrouper les chaînes manipulant des chaînes longues dans
plusieurs catégories fonctionnelles. Dans ces catégories, certaines routines sont
utilisées dans le même but mais varient dans l’utilisation de critères particuliers
dans leurs calculs. Les tableaux ci-dessous énumèrent ces routines en les
regroupant selon les catégories suivantes :
• Comparaison
• Conversions majuscules/minuscules
• Modification
• Sous-chaîne
Quand c’est approprié, les tableaux indiquent également si la routine satisfait les
critères suivants :
• Différenciation majuscules/minuscules : si la localisation Windows est utilisée,
elle détermine la définition des caractères majuscules/minuscules. Si la routine
n’utilise pas la localisation Windows, l’analyse se fonde sur la valeur scalaire
des caractères. Si la routine ne tient pas compte des différences majuscules/
minuscules, il y a une fusion logique des caractères majuscules et minuscules
déterminée par un modèle prédéfini.
• Utilisation de la localisation Windows : cela permet d’ajouter des
caractéristiques supplémentaires à votre application pour des localisations
spécifiques, en particulier dans le cas des environnements pour les langues
asiatiques. Dans la plupart des localisations Windows, les caractères minuscules
sont censés être inférieurs aux caractères majuscules correspondants. C’est
l’opposé de l’ordre ASCII dans lequel les caractères minuscules sont supérieurs
aux caractères majuscules. Les routines utilisant la localisation Windows sont
préfixées par le mot "Ansi" (c’est-à-dire de la forme AnsiXXX).

3-30 Guide du développeur


Utilisation des chaînes

Gestion des jeux de caractères multi-octets (MBCS) : les MBCS sont utilisés pour
écrire du code pour les localisations extrême-orientales. Les caractères multi-octets
sont représentés par un mélange de codes de caractère sur un ou deux octets, le
nombre d’octets ne correspond donc pas systématiquement à la longueur de la
chaîne. Les routines qui gèrent les MBCS analysent les caractères sur un ou deux
octets. ByteType et StrByteType déterminent si un octet donné est l’octet de tête
d’un caractère sur deux octets. Faites attention en manipulant des caractères
multi-octets à ne pas tronquer une chaîne en coupant en deux un caractère sur
deux octets. Ne transmettez pas de caractères comme paramètre d’une fonction
ou d’une procédure puisque la taille d’un caractère ne peut être déterminée à
l’avance. Il faut, à la place, transmettre un pointeur sur un caractère ou une
chaîne. Pour davantage d’informations sur MBCS, voir “Codage de l’application”
à la page 2 du chapitre 10, “Création d’applications internationales.”.

Tableau 3.3 Routines de comparaison de chaînes


Utilisation de la
Différence MAJ/ localisation Gestion
Routine min Windows MBCS
AnsiCompareStr Oui Oui Oui
AnsiCompareText Non Oui Oui
AnsiCompareFileName Non Oui Oui
CompareStr Oui Non Non
CompareText Non Non Non

Tableau 3.4 Routines de conversions majuscules/minuscules


Utilisation de la
Routine localisation Windows Gestion MBCS
AnsiLowerCase Oui Oui
AnsiLowerCaseFileName Oui Oui
AnsiUpperCaseFileName Oui Oui
AnsiUpperCase Oui Oui
LowerCase Non Non
UpperCase Non Non

Tableau 3.5 Routines de modification de chaîne


Routine Différence MAJ/min Gestion MBCS
AdjustLineBreaks NA Oui
AnsiQuotedStr NA Oui
StringReplace précisé par un indicateur Oui
Trim NA Oui
TrimLeft NA Oui
TrimRight NA Oui
WrapText NA Oui

Sujets de programmation généraux 3-31


Utilisation des chaînes

Tableau 3.6 Routines de sous-chaîne


Routine Différence MAJ/min Gestion MBCS
AnsiExtractQuotedStr ND Oui
AnsiPos Oui Oui
IsDelimiter Oui Oui
IsPathDelimiter Oui Oui
LastDelimiter Oui Oui
QuotedStr Non Non

Les routines utilisées pour les noms de fichier sous forme de chaîne :
AnsiCompareFileName, AnsiLowerCaseFileName et AnsiUpperCaseFileName utilisent
toutes la localisation Windows. Vous devez toujours utiliser des noms de fichier
totalement portables car la localisation (le jeu de caractères) utilisée pour les
noms de fichier peut changer selon l’interface utilisateur par défaut.

Déclaration et initialisation de chaînes


Quand vous déclarez une chaîne longue :
S: string;
il n’est pas nécessaire de l’initialiser. Les chaînes longues sont automatiquement
initialisées vides. Pour tester si une chaîne longue est vide, vous pouvez utiliser
la variable EmptyStr :
S = EmptyStr;
ou la comparer à une chaîne vide :
S = ‘’;
Une chaîne vide ne contient pas de données utilisables. Donc, essayer d’accéder
par indice à une chaîne vide est similaire à l’accès à nil et provoque une
violation d’accès :
var
S: string;
begin
S[i];// cela provoque une violation d’accès
// instructions
end;
De même, si vous transtypez une chaîne vide en un PChar, le résultat est un
pointeur nil. Donc, si vous transmettez un tel PChar à une routine qui doit le lire
ou l’écrire, la routine doit gérer la valeur nil :
var
S: string;// chaîne vide
begin
proc(PChar(S));// assurez-vous que proc peut gérer nil
// instructions
end;

3-32 Guide du développeur


Utilisation des chaînes

Si ce n’est pas le cas, vous devez initialiser la chaîne :


S := ‘plus nil’;
proc(PChar(S));// proc n’a plus besoin de gérer nil
ou vous devez en spécifier la longueur en utilisant la procédure SetLength :
SetLength(S, 100);// attribue à la chaîne S une longueur dynamique 100
proc(PChar(S)); // proc n’a plus besoin de gérer nil
Quand vous utilisez SetLength, les caractères existant déjà dans la chaîne sont
préservés mais le contenu de l’espace nouvellement alloué est indéterminé. Après
un appel à SetLength, S référence obligatoirement une chaîne unique, c’est-à-dire
une chaîne dont le compteur de référence a la valeur un. Pour connaître la
longueur d’une chaîne, utilisez la fonction Length.
N’oubliez pas qu’une chaîne string déclarée de la manière suivante :
S: string[n];
est implicitement une chaîne courte et pas une chaîne longue de longueur n.
Pour déclarer une chaîne longue ayant spécifiquement la longueur n, déclarez
une variable de type string, puis utilisez la procédure SetLength :
S: string;
SetLength(S, n);

Mélange et conversion de types chaîne


Il est possible de mélanger dans des expressions et des affectations des chaînes
longues et des chaînes étendues ; le compilateur génère automatiquement le code
pour effectuer les conversions de type chaîne nécessaires. Par contre, si vous
affectez une valeur chaîne à une variable chaîne courte, n’oubliez pas que la
valeur chaîne est tronquée si elle excède la longueur maximum déclarée de la
variable de type chaîne courte.
Les chaînes longues sont déjà allouées dynamiquement. N’oubliez pas que si
vous utilisez l’un des types de pointeur prédéfinis (comme PAnsiString, PString
ou PWideString), vous introduisez un niveau d’indirection supplémentaire. Vous
ne devez le faire qu’en connaissance de cause.

Conversions de chaînes en PChar


La conversion de chaînes longues en PChar n’est pas effectuée automatiquement.
Certaines différences entre les chaînes et les PChar peuvent rendre la conversion
problématique :
• Les chaînes longues utilisent le comptage de références, mais pas les PChar.
• L’affectation d’une chaîne longue copie les données alors qu’un PChar est un
pointeur sur la mémoire.
• Les chaînes longues sont à zéro terminales et contiennent également la
longueur de la chaîne alors que les PChars sont seulement à zéro terminal.
Cette section présente ce qui dans ces différences peut causer des erreurs délicates.

Sujets de programmation généraux 3-33


Utilisation des chaînes

Dépendances de chaîne
Il est parfois nécessaire de convertir des chaînes longues en chaînes à zéro
terminal, par exemple si vous utilisez une fonction qui attend un PChar.
Cependant, comme les chaînes longues utilisent le comptage de références, le
transtypage d’une chaîne en un PChar augmente de un les dépendances de la
chaîne sans augmenter également le compteur de références. Quand le compteur
de références atteint zéro, la chaîne est détruite même s’il y a encore des
dépendances portant dessus. Le transtypage en PChar disparaît également, et ce
alors même que la routine à laquelle vous l’avez transmis l’utilise peut-être
encore. Si vous transtypez une chaîne en un PChar, c’est vous qui êtes
responsable de la durée de vie du PChar résultant. Par exemple :
procedure my_func(x: string);
begin
// faire quelque chose avec x
some_proc(PChar(x)); // transtype la chaîne en PChar
// vous devez maintenant garantir que la chaîne existe
// tant que la procédure some_proc a besoin de l’utiliser
end;

Renvoi d’une variable locale PChar


Une erreur courante quand vous utilisez un PChar est de stocker dans une
structure de données ou de renvoyer comme résultat une variable locale. Une
fois votre routine achevée, le PChar disparaît car c’est un simple pointeur
mémoire et non une copie de chaîne utilisant le comptage de références. Par
exemple :
function title(n: Integer): PChar;
var
s: string;
begin
s := Format(‘titre - %d’, [n]);
Result := PChar(s); // A NE PAS FAIRE
end;
Cet exemple renvoie un pointeur sur une donnée chaîne qui est libérée à
l’achèvement de la fonction title.

Transfert d’une variable locale comme PChar


Si vous avez une variable locale chaîne qui doit être initialisée en appelant une
fonction qui attend un paramètre PChar. Une solution consiste à créer une
variable locale array of char et à la transmettre à la fonction. Il faut ensuite
affecter cette variable à la chaîne :
// MAXSIZE est une constante définie par ailleurs
var
i: Integer;
buf: array[0..MAX_SIZE] of char;
S: string;

3-34 Guide du développeur


Utilisation des chaînes

begin
i := GetModuleFilename(0, @buf, SizeOf(buf));// traite @buf comme un PChar
S := buf;
// instructions
end;
Cette manière de procéder est envisageable si la taille du tampon reste
suffisamment petite pour être allouée sur la pile. Elle est également fiable car la
conversion est automatique entre un type array of char et un type string. A la
fin de l’exécution de GetModuleFilename, la longueur de la chaîne indique
correctement le nombre d’octets écrits dans buf.
Pour éliminer le surcoût de la copie du tampon, il est possible de transtyper la
chaîne en un PChar (si vous êtes certain que la routine n’a pas besoin que le PChar
reste en mémoire). Mais dans ce cas, la synchronisation de la longueur de la
chaîne n’est pas effectuée automatiquement comme c’est le cas lors de l’affectation
de array of char vers string. Vous devez réinitialiser la longueur de la chaîne afin
de refléter la longueur réelle de la chaîne. Si vous utilisez une fonction qui renvoie
le nombre d’octets copiés, une seule ligne de code suffit à le faire :
var
S: string;
begin
SetLength(S, 100);// avant de transtyper en PChar, vérifiez que la chaîne n’est pas vide
SetLength(S, GetModuleFilename( 0, PChar(S), Length(S) ) );
// instructions
end;

Directives de compilation portant sur les chaînes


Les directives de compilation suivantes affectent les types caractère et chaîne.
• {$H+/-} : La directive de compilation $H contrôle si le mot réservé string
représente une chaîne courte ou une chaîne longue. A l’état par défaut, {$H+},
string représente une chaîne longue. Vous pouvez le changer en ShortString en
utilisant la directive {$H.
• {$P+/-} : La directive $P, proposée pour la compatibilité ascendante avec les
versions précédentes de Delphi et de Borland Pascal, n’a de sens que pour le
code compilé à l’état {$H-}. $P contrôle la signification des paramètres var
déclarés en utilisant le mot réservé string en étant à l’état {$H-}. A l’état {$P-},
les paramètres variables déclarés en utilisant le mot réservé string sont des
paramètres variables normaux. Par contre, à l’état {$P+}, ce sont des
paramètres chaîne ouverte. Indépendamment de l’état de la directive $P, il est
toujours possible d’utiliser l’identificateur OpenString pour déclarer des
paramètres chaîne ouverte. Effectuez un lien vers les directives de compilation.
• {$V+/-} : La directive $V contrôle comment s’effectue la vérification de type
pour les paramètres chaîne courte transmis comme paramètre variable. A l’état
{$V+}, une vérification de type stricte est effectuée et exige que les paramètres
formel et réel soient exactement du même type chaîne. A l’état {$V-}, toute
chaîne courte est autorisée comme paramètre réel, même si sa longueur

Sujets de programmation généraux 3-35


Utilisation des fichiers

maximum déclarée n’est pas la même que celle du paramètre formel.


Attention : cela peut entraîner une corruption de la mémoire. Par exemple :
var S: string[3];
procedure Test(var T: string);
begin
T := ‘1234’;
end;
begin
Test(S);
end.
• {$X+/-} : La directive de compilation {$X+} permet à Delphi de gérer les
chaînes à zéro terminal en activant des règles particulières qui s’appliquent au
type prédéfini PChar et aux tableaux de caractères d’indice de base zéro. Ces
règles permettent d’utiliser des tableaux de caractères d’indice de base zéro ou
des pointeurs de caractère avec les routines Write, Writeln, Val, Assign et
Rename de l’unité System.

Chaînes et caractères : sujets apparentés


Les rubriques suivantes du guide du langage Pascal Objet abordent les chaînes et
les jeux de caractères. Voir aussi chapitre 10, “Création d’applications
internationales.”
• “A propos des jeux de caractères étendus.” (Aborde les jeux de caractères
internationaux.)
• “Utilisation de chaînes à zéro terminal.” (Contient des informations sur les
tableaux de caractères.)
• “Chaînes de caractères.”
• “Pointeurs de caractères.”
• “Opérateurs de chaînes.”

Utilisation des fichiers


Cette section décrit les manipulations de fichier en distinguant entre la
manipulation de fichiers disque et les opérations d’entrées/sorties comme la
lecture ou l’écriture dans des fichiers. La première section décrit les routines de
la bibliothèque d’exécution et de l’API Windows que vous utiliserez pour des
opérations courantes impliquant la manipulation de fichiers sur disque. La
section suivante est une présentation des types fichier utilisés pour les entrées/
sorties. Enfin, la dernière section présente la méthode conseillée pour manipuler
les entrées/sorties de fichier, à savoir l’utilisation de flux fichier.
Remarque Les versions précédentes du langage Pascal Objet effectuaient les opérations sur les
fichiers mêmes et non pas sur des paramètres nom de fichier, comme c’est le cas
aujourd’hui. Avec les anciens types fichier, vous deviez trouver le fichier, l’affecter
à une variable fichier avant de pouvoir, par exemple, renommer le fichier.

3-36 Guide du développeur


Utilisation des fichiers

Manipulation de fichiers
Plusieurs opérations courantes portant sur les fichiers sont prédéfinies dans la
bibliothèque d’exécution Pascal Objet. Les procédures et fonctions manipulant les
fichiers agissent à un niveau élevé. Pour la plupart des routines, il vous suffit de
spécifier le nom du fichier et la routine fait pour vous les appels nécessaires au
système d’exploitation. Dans certains cas, vous utiliserez à la place des handles de
fichier. Le Pascal Objet propose des routines pour la plupart des manipulations de
fichier. Quand ce n’est pas le cas, d’autres routines sont décrites.

Suppression d’un fichier


La suppression efface un fichier du disque et retire son entrée du répertoire du
disque. Il n’y a pas d’opération inverse pour restaurer un fichier supprimé : les
applications doivent donc généralement demander à l’utilisateur de confirmer les
suppressions de fichiers. Pour supprimer un fichier, transmettez le nom du
fichier à la fonction DeleteFile:
DeleteFile(NomFichier);
DeleteFile renvoie True si le fichier a été supprimé et False sinon (par exemple, si
le fichier n’existe pas ou s’il est en lecture seule). DeleteFile supprime le fichier
du disque nommé NomFichier.

Recherche d’un fichier


Trois routines permettent de chercher un fichier : FindFirst, FindNext et FindClose.
FindFirst recherche la première instance d’un nom de fichier ayant un ensemble
spécifié d’attributs dans un répertoire spécifié. FindNext renvoie l’entrée suivante
correspondant au nom et aux attributs spécifiés dans un appel précédent de
FindFirst. FindClose libère la mémoire allouée par FindFirst. Dans Windows 32 bits
vous devez toujours utiliser FindClose pour clore une séquence FindFirst/
FindNext. Si vous voulez simplement savoir si un fichier existe, utilisez la
fonction FileExists qui renvoie True si le fichier existe et False sinon.
Les trois routines de recherche de fichier attendent dans leurs paramètres un
TSearchRec. TSearchRec définit les informations du fichier recherché par FindFirst
ou FindNext. TSearchRec a la déclaration suivante :
type
TFileName = string;
TSearchRec = record
Time: Integer;// Time contient l’indicateur horaire du fichier.
Size: Integer;// Size contient la taille, en octets, du fichier.
Attr: Integer;// Attr représente les attributs du fichier.
Name: TFileName;//Name contient le nom de fichier DOS et l’extension.
ExcludeAttr: Integer;
FindHandle: THandle;
FindData: TWin32FindData;// FindData contient des informations complémentaires
// comme l’heure de création du fichier, l’heure du dernier accès, les noms de fichier
// long et court.
end;

Sujets de programmation généraux 3-37


Utilisation des fichiers

Si un fichier est trouvé, les champs du paramètre de type TSearchRec sont


modifiés pour spécifier le fichier trouvé. Vous pouvez comparer Attr aux
constantes ou aux valeurs d’attributs suivantes afin de déterminer si un fichier a
un attribut donné :

Tableau 3.7 Constantes et valeur d’attribut


Constante Valeur Description
faReadOnly $00000001 Fichiers en lecture seule
faHidden $00000002 Fichiers cachés
faSysFile $00000004 Fichiers système
faVolumeID $00000008 Fichier ID de volume
faDirectory $00000010 Fichiers répertoire
faArchive $00000020 Fichier archive
faAnyFile $0000003F Tout fichier

Pour tester un attribut, combinez la valeur du champ Attr et la constante


d’attribut avec l’opérateur and. Si le fichier a cet attribut, le résultat est supérieur
à 0. Par exemple, si le fichier trouvé est un fichier caché, l’expression suivante a
la valeur True : (SearchRec.Attr and faHidden > 0). Il est possible de combiner les
attributs en ajoutant leur constante ou leur valeur. Par exemple, pour rechercher
les fichiers en lecture seule et les fichiers cachés en plus des fichiers normaux,
transmettez (faReadOnly + faHidden) au paramètre Attr.
Exemple Cet exemple utilise une fiche contenant un libellé, un bouton nommé Search et un
bouton nommé Again. Quand l’utilisateur clique sur le bouton Search, le premier
fichier du répertoire spécifié est trouvé et son nom et sa taille en octets sont
affichés dans l’intitulé du libellé. A chaque fois que l’utilisateur clique sur le
bouton Again, le nom et la taille du fichier correspondant suivant sont affichés
dans le libellé :
var
SearchRec: TSearchRec;
procedure TForm1.SearchClick(Sender: TObject);
begin
FindFirst('c:\Program Files\delphi4\bin\*.*', faAnyFile, SearchRec);
Label1.Caption := SearchRec.Name + ' occupe ' + IntToStr(SearchRec.Size) + ' octets';
end;
procedure TForm1.AgainClick(Sender: TObject);
begin
if (FindNext(SearchRec) = 0)
Label1.Caption := SearchRec.Name + ' occupe ' + IntToStr(SearchRec.Size) + '
octets';
else
FindClose(SearchRec);
end;

3-38 Guide du développeur


Utilisation des fichiers

Modifications des attributs d’un fichier


Chaque fichier a plusieurs attributs stockés par le système d’exploitation sous la
forme d’indicateurs correspondant à des bits. Les attributs de fichier indiquent
par exemple si un fichier est en lecture seule ou si c’est un fichier caché. La
modification des attributs d’un fichier passe par trois étapes : lecture,
modification et affectation.
Lecture des attributs de fichier : les systèmes d’exploitation stockent les attributs
de fichier de diverses manières, généralement sous la forme d’indicateurs
correspondant à des bits. Pour lire les attributs d’un fichier, transmettez le nom
du fichier à la fonction FileGetAttr qui renvoie les attributs d’un fichier. La valeur
renvoyée de type Word est un groupe d’attributs de fichier correspondant à des
bits. Il est possible d’examiner les divers attributs en les combinant à l’aide de
l’opérateur and avec les constantes définies dans TSearchRec. Si la valeur
renvoyée est -1, cela signifie qu’il y a eu une erreur.
Modification individuelle d’attributs de fichier : comme Delphi représente les
attributs sous la forme d’un ensemble, vous pouvez utiliser les opérations
logiques normales pour manipuler individuellement les attributs. Chaque attribut
a un nom mnémonique défini dans l’unité SysUtils. Par exemple, pour attribuer à
un fichier l’attribut en lecture seule, vous devez procéder de la manière
suivante :
Attributes := Attributes or faReadOnly;
Vous pouvez également définir ou effacer plusieurs attributs à la fois. Ainsi,
l’instruction suivante efface simultanément les attributs fichier système et fichier
caché :
Attributes := Attributes and not (faSysFile or faHidden);
Affectation des attributs de fichier : Delphi vous permet de spécifier les
attributs de tout fichier à tout moment. Pour initialiser les attributs d’un fichier,
transmettez le nom du fichier et les attributs souhaités à la fonction FileSetAttr.
FileSetAttr initialise les attributs du fichier spécifié.
Vous pouvez utiliser indépendamment les opérations de lecture et d’écriture ; si
vous voulez simplement déterminer les attributs d’un fichier ou si vous voulez
les initialiser sans tenir compte des valeurs antérieures. Pour modifier les
attributs en se basant sur les valeurs antérieures, vous devez lire les attributs
existants, les modifier puis écrire les attributs modifiés.

Modification d’un nom de fichier


Pour changer le nom d’un fichier, utilisez simplement la fonction RenameFile :
function RenameFile(const AncienNomFichier, NouveauNomFichier: string): Boolean;
qui renomme le fichier nommé AncienNomFichier en NouveauNomFichier. Si
l’opération réussit, RenameFile renvoie True. Si le fichier ne peut être renommé,
par exemple parce qu’il existe déjà un fichier portant le nom NouveauNomFichier,
elle renvoie False. Par exemple :
if not RenameFile('ANCNOM.TXT','NOUVNOM.TXT') then
ErrorMsg('Erreur en renommant le fichier!');

Sujets de programmation généraux 3-39


Utilisation des fichiers

Il n’est pas possible de renommer (déplacer) un fichier entre des lecteurs en


utilisant RenameFile. Pour ce faire, vous devez commencer par copier le fichier,
puis supprimer le fichier original.
Remarque RenameFile encapsule la fonction MoveFile de l’API Windows :donc MoveFile ne
fonctionne également pas entre des lecteurs.

Routines date-heure de fichier


Les routines FileAge, FileGetDate et FileSetDate agissent sur les valeurs date-heure
du système d’exploitation. FileAge renvoie l’indicateur de date et d’heure d’un
fichier ou -1 si le fichier est inexistant. FileSetDate définit l’indicateur de date et
d’heure du fichier spécifié et renvoie zéro en cas de réussite et un code d’erreur
Windows en cas d’échec. FileGetDate renvoie l’indicateur de date et d’heure d’un
fichier ou -1 si le handle est invalide.
Comme la plupart des routines de manipulation de fichier, FileAge utilise un
nom de fichier sous forme de chaîne. Par contre, FileGetDate et FileSetDate
attendent un paramètre de type handle Windows. Pour accéder au handle
Windows d’un fichier vous pouvez :
• appelez la fonction CreateFile de l’API Windows. CreateFile est une fonction
uniquement 32 bits qui crée et ouvre un fichier et renvoie un handle qui peut
être utilisé pour accéder au fichier.
• ou, instanciez un TFileStream pour créer et ouvrir un fichier. Utilisez ensuite sa
propriété Handle comme vous le feriez d’un handle de fichier Windows. Pour
davantage d’informations, voir “Utilisation des flux fichier” à la page 3-41.

Copie d’un fichier


La bibliothèque d’exécution ne propose aucune routine pour copier un fichier.
Vous pouvez cependant appeler directement la fonction CopyFile de l’API
Windows pour copier un fichier. Comme la plupart des routines de
manipulation de fichier de la bibliothèque d’exécution Delphi, CopyFile prend
comme paramètre un nom de fichier et pas un handle Windows. Quand vous
copiez un fichier, n’oubliez pas que les attributs du fichier existant sont copiés
dans le nouveau fichier, mais pas ses attributs de sécurité. CopyFile sert
également pour déplacer des fichiers entre des lecteurs car ni la fonction Delphi
RenameFile, ni la fonction API Windows MoveFile ne peut renommer/déplacer
des fichiers entre des lecteurs. Pour davantage d’informations, voir l’aide en
ligne Microsoft Windows.

Types fichier et E/S de fichier


Vous pouvez utiliser trois types fichier pour les entrées/sorties (E/S) de fichier :
les anciens styles de fichier Pascal, les handles de fichier Windows et les objets
flux fichier. Cette section décrit ces trois types.
Fichiers Pascal ancien style : ce sont les types fichier utilisés pour les anciennes
variables fichier, généralement de la forme "F: Text: ou "F: File". Il existe trois
formes de ces fichiers : typé, texte et sans type. De nombreuses routines de

3-40 Guide du développeur


Utilisation des fichiers

gestion de fichier Delphi, comme AssignPrn ou writeln, les utilisent. Ces types
fichier sont obsolètes et sont incompatibles avec les handles de fichier Windows.
Si vous avez besoin de travailler avec ces types de fichier anciens, consultez le
Guide du langage Pascal Objet .
Handles de fichier Windows : les handles de fichier Pascal Objet encapsulent le
type de handle de fichier Windows. Les routines de manipulation de fichier de
la bibliothèque d’exécution qui utilisent des handles de fichier Windows
encapsulent généralement des fonctions de l’API Windows. Ainsi, FileRead
appelle la fonction Windows ReadFile. Comme les fonctions Delphi utilisent la
syntaxe Pascal Objet et peuvent spécifier la valeur par défaut de paramètres,
elles peuvent servir commodément d’interface avec l’API Windows. L’utilisation
de ces routines est évidente et, si vous êtes à l’aise avec les routines de fichier de
l’API Windows, vous pouvez les utiliser pour manipuler les E/S de fichier.
Flux fichier : les flux fichier sont des instances d’objet de la classe TFileStream de
la VCL utilisées pour accéder aux informations de fichiers disque. Les flux fichier
sont portables et proposent une approche de haut niveau des opérations d’E/S
de fichier. TFileStream a une propriété Handle qui vous donne accès au handle de
fichier Windows. La section suivante décrit TFileStream.

Utilisation des flux fichier


TFileStream est une classe VCL utilisée pour une représentation objet de haut
niveau des flux fichier. TFileStream offre de nombreuses fonctionnalités : la
persistance, l’interaction avec d’autres flux et les E/S de fichier.
• TFileStream est un descendant des classes flux. En tant que tel l’héritage
automatique de la gestion de la persistance est un des avantages de
l’utilisation des flux fichier. Les classes flux peuvent travailler avec les classes
TFiler, TReader et TWriter, pour lire des flux d’objets depuis un disque. Donc,
quand vous avez un flux fichier, vous pouvez utiliser le même code que celui
employé par le mécanisme de flux de la VCL. Pour davantage d’informations
sur l’utilisation du système de flux de la VCL, voir dans la version en ligne
du Guide de référence de la VCL, les rubriques concernant les classes TStream,
TFiler, TReader, TWriter et TComponent.
• TFileStream peut facilement interagir avec d’autres classes flux. Si, par
exemple, vous voulez écrire sur disque un bloc de mémoire dynamique, vous
pouvez le faire en utilisant un TFileStream et un TMemoryStream.
• TFileStream propose les méthodes et propriétés de base pour les entrées/
sorties de fichier. Les sections suivantes abordent cet aspect des flux fichier

Création et ouverture de fichiers


Pour créer ou ouvrir un fichier et disposer d’un handle pour le fichier, il suffit
d’instancier un TFileStream. Cela crée ou ouvre le fichier spécifié et propose des
méthodes qui permettent de lire ou d’écrire dans le fichier. Si le fichier ne peut
être ouvert, TFileStream déclenche une exception.
constructor Create(const filename: string; Mode: Word);

Sujets de programmation généraux 3-41


Utilisation des fichiers

Le paramètre Mode spécifie comment le fichier doit être ouvert à la création du flux
fichier. Le paramètre Mode est constitué d’un mode d’ouverture et d’un mode de
partage reliés par un ou logique. Le mode d’ouverture doit prendre l’une des valeurs
suivantes :

Valeur Signification
fmCreate TFileStream crée un fichier portant le nom spécifié. S’il existe déjà un
fichier portant ce nom, il est ouvert en mode écriture.
fmOpenRead Ouvre le fichier en lecture seulement.
fmOpenWrite Ouvre le fichier en écriture seulement. L’écriture dans le fichier
remplace son contenu actuel.
fmOpenReadWrite Ouvre le fichier pour en modifier le contenu et non pour le remplacer.

Le mode de partage peut prendre l’une des valeurs suivantes:

Valeur Signification
fmShareCompat Le partage est compatible avec la manière d’ouvrir les FCB.
fmShareExclusive En aucun cas, les autres applications ne peuvent ouvrir le fichier.
fmShareDenyWrite Les autres applications peuvent ouvrir le fichier en lecture, mais pas en
écriture.
fmShareDenyRead Les autres applications peuvent ouvrir le fichier en écriture, mais pas en
lecture.
fmShareDenyNone Rien n’empêche les autres applications de lire ou d’écrire dans le fichier.

Les constantes d’ouverture et de partage de fichier sont définies dans l’unité SysUtils.

Utilisation du handle de fichier


Quand vous instanciez TFileStream, vous avez accès au handle de fichier. Le
handle de fichier est contenu dans la propriété Handle. Handle est en lecture seule
et indique le mode d’ouverture du fichier. Si vous voulez modifier les attributs
du handle de fichier, vous devez créer un nouvel objet flux fichier.
Certaines routines de manipulation de fichier attendent comme paramètre un
handle de fichier Windows. Quand vous disposez d’un flux fichier, vous pouvez
utiliser la propriété Handle dans toutes les situations où vous utiliseriez un
handle de fichier Windows. Attention, à la différence des handles de flux, les
flux fichiers ferment le handle de fichier quand l’objet est détruit.

Lecture et écriture de fichiers


TFileStream dispose de plusieurs méthodes permettant de lire et d’écrire dans les
fichiers. Elles sont différenciées selon qu’elles se trouvent dans l’une ou l’autre
des situations suivantes :
• Renvoi du nombre d’octets lus ou écrits.
• Nécessité de connaître le nombre d’octets.
• Déclenchement d’une exception en cas d’erreur.

3-42 Guide du développeur


Utilisation des fichiers

Read est une fonction qui lit jusqu’à Count octets dans le fichier associé à un flux
fichier en commençant à la position en cours (Position) et les place dans Buffer.
Read déplace ensuite la position en cours dans le fichier du nombre d’octets
effectivement lus. Read a le prototype suivant :
function Read(var Buffer; Count: Longint): Longint; override ;
Read est utile quand le nombre d’octets du fichier est inconnu. Read renvoie le
nombre d’octets effectivement transférés qui peut être inférieur à Count si le
marqueur de fin de fichier a été atteint.
Write est une fonction qui écrit Count octets de Buffer dans le fichier associé au
flux fichier en commençant à la position en cours (Position). Write a le prototype
suivant :
function Write(const Buffer; Count: Longint): Longint; override ;
Après avoir écrit dans le fichier, Write avance la position en cours dans le fichier
du nombre d’octets écrits et renvoie le nombre d’octets effectivement écrits qui
peut être inférieur à Count si la fin du tampon a été atteinte.
La paire de procédures correspondantes ReadBuffer et WriteBuffer ne renvoient
pas comme Read et Write le nombre d’octets lus ou écrits. Ces procédures sont
utiles dans les situations où le nombre d’octet est connu et obligatoire : par
exemple pour la lecture de structures. ReadBuffer et WriteBuffer déclenchent une
exception en cas d’erreur (EReadError et EWriteError), alors que les méthodes
Read et Write ne le font pas. Les prototypes de ReadBuffer et WriteBuffer sont :
procedure ReadBuffer(var Buffer; Count: Longint);
procedure WriteBuffer(const Buffer; Count: Longint);
Ces méthodes appellent les méthodes Read et Write pour réaliser la lecture ou
l’écriture.

Lecture et écriture de chaînes


Si vous transmettez une chaîne à une fonction de lecture ou d’écriture, vous
devez utiliser la bonne syntaxe. Les paramètres Buffer des routines de lecture et
d’écriture sont des paramètres, respectivement, var et const. Ce sont des
paramètres sans type, les routines utilisent donc l’adresse des variables.
Le type chaîne longue est le plus couramment utilisé pour la manipulation de
chaînes. Cependant, la transmission d’une chaîne longue comme paramètre Buffer
ne génère pas le résultat adéquat. Les chaînes longues contiennent une taille, un
compteur de références et un pointeur sur les caractères de la chaîne. De ce fait,
déréférencer une chaîne longue ne donne pas seulement l’élément pointeur. Vous
devez tout d’abord transtyper la chaîne en Pointer ou un PChar, puis alors
seulement la déréférencer. Par exemple :
procedure cast-string;
var
fs: TFileStream;
s: string = 'Hello';

Sujets de programmation généraux 3-43


Utilisation des fichiers

begin
fs := TFileStream.Create('Temp.txt', fmCreate or fmOpenWrite);
fs.Write(s, Length(s));// cela donne un résultat faux
fs.Write(PChar(s)^, Length(s));// Cela donne le bon résultat
end;

Déplacements dans un fichier


La plupart des mécanismes d’E/S de fichier proposent le moyen de se déplacer à
l’intérieur d’un fichier afin de pouvoir lire ou écrire à un emplacement
particulier du fichier. TFileStream propose pour ce faire la méthode Seek. Seek a le
prototype suivant :
function Seek(Offset: Longint; Origin: Word): Longint; override ;
Le paramètre Origin indique la manière d’interpréter le paramètre Offset. Origin peut
prendre l’une des valeurs suivantes :

Valeur Signification
soFromBeginning Offset part du début de la ressource. Seek place à la position Offset. Offset doit
être >= 0.
soFromCurrent Offset part de la position en cours dans la ressource. Seek place à la position
Position + Offset.
soFromEnd Offset part de la fin de la ressource. Offset doit être <= 0 afin d’indiquer le
nombre d’octets avant la fin du fichier.

Seek réinitialise la valeur de Position dans le flux en la déplaçant du décalage


spécifié. Seek renvoie la nouvelle valeur de la propriété Position : la nouvelle
position en cours dans la ressource.

Position et taille de fichier


TFileStream dispose de propriétés qui contiennent la position en cours dans le
fichier et sa taille. Ces propriétés sont utilisées par les méthodes de lecture et
d’écriture et par Seek.
La propriété Position de TFileStream est utilisée pour indiquer le décalage en
cours dans le flux, exprimé en octets, à partir du début des données du flux.
Position a la déclaration suivante :
property Position: Longint;
La propriété Size indique la taille en octets du flux. Elle est utilisée comme
marqueur de fin de fichier afin de tronquer le fichier. Size a la déclaration suivante :
property Size: Longint;
Size est utilisée de manière interne par les routines qui lisent et écrivent dans le
flux.
L’initialisation de la propriété Size modifie la taille du fichier. Si la taille du
fichier ne peut être modifiée, une exception est déclenchée. Ainsi, tenter de
modifier la taille d’un fichier ouvert dans le mode fmOpenRead déclenche une
exception.

3-44 Guide du développeur


Définition de nouveaux types de données

Copie
CopyFrom copie le nombre spécifié d’octets d’un flux (fichier) dans un autre.
function CopyFrom(Source: TStream; Count: Longint): Longint;
L’utilisation de CopyFrom évite à l’utilisateur qui veut copier des données de
créer un tampon, d’y placer les données, de les écrire puis de libérer le tampon.
CopyFrom copie Count octets de Source dans le flux. CopyFrom déplace ensuite la
position en cours de Count octets et renvoie le nombre d’octets copiés. Si Count vaut
0, CopyFrom initialise la position dans Source à 0 avant de lire puis de copier dans le
flux la totalité des données de Source. Si Count est supérieur ou inférieur à 0,
CopyFrom lit à partir de la position en cours dans Source.

Définition de nouveaux types de données


Pascal Objet dispose de nombreux types de données prédéfinis. Vous pouvez
utiliser ces types prédéfinis pour créer de nouveaux types qui correspondent aux
besoins spécifiques de votre application. Pour une présentation de ces types,voir
le Guide du langage Pascal Objet.

Sujets de programmation généraux 3-45


3-46 Guide du développeur
Chapitre

Création d’applications,
Chapter 4
4
de composants
et de bibliothèques
Ce chapitre donne un aperçu de la manière d’utiliser Delphi pour créer des
applications, des bibliothèques et des composants.

Création d’applications
L’utilisation principale de Delphi est la conception et la génération d’applications
Windows. Il y a trois types de base d’application Windows :
• Les applications d’interface utilisateur graphique Windows
• Les applications console
• Les applications service

Applications Windows
Quand vous compilez un projet, un fichier exécutable (.EXE) est créé.
Généralement, l’exécutable fournit les fonctions de base de votre programme et
les programmes simples sont souvent composés uniquement d’un EXE. Vous
pouvez aussi étendre une application en appelant des DLL, des paquets ou
d’autres bibliothèques complétant un exécutable.
Windows offre deux modèles d’interface utilisateur d’application :
• L’interface de document unique (abrégé en anglais par SDI)
• L’interface de document multiple (abrégé en anglais par MDI)

Création d’applications, de composants et de bibliothèques 4-1


Création d’applications

Outre le modèle d’implémentation de votre application, le comportement de


votre projet à la conception, comme celui de l’application à l’exécution, peut être
manipulé par des options de projet de l’EDI.

Modèles d’interfaces utilisateur


Toute fiche peut être implémentée comme une fiche d’interface de document
multiple (MDI) ou comme une fiche d’interface de document unique (SDI). Dans
une application MDI, plusieurs documents ou fenêtres enfant peuvent être
ouverts dans une seule fenêtre parent. Cela est courant dans les applications
comme les tableurs ou les traitements de texte. Par contre, une application SDI
ne contient normalement qu’une seule vue de document. Pour faire de votre
fiche une application SDI, affectez la valeur fsNormal à la propriété FormStyle de
votre objet Form.
Pour davantage d’informations sur le développement de l’interface utilisateur d’une
application, voir chapitre 5, “Conception de l’interface utilisateur des applications”

Applications SDI
Pour créer une nouvelle application SDI :
1 Sélectionnez Fichier|Nouveau pour afficher la boîte de dialogue Nouveaux
éléments.
2 Cliquez sur l’onglet Projets et sélectionnez Application SDI.
3 Choisissez OK.
Par défaut, la propriété FormStyle de l’objet Form a la valeur fsNormal, Delphi
suppose que toute nouvelle application est une application SDI.

Applications MDI
Pour créer une nouvelle application MDI :
1 Sélectionnez Fichier|Nouveau pour afficher la boîte de dialogue Nouveaux
éléments.
2 Cliquez sur l’onglet Projets et sélectionnez Application MDI.
3 Choisissez OK.
Les applications MDI nécessitent plus de réflexion et sont plus complexes à
concevoir que les applications SDI. Les applications MDI contiennent des fenêtres
enfant qui se trouvent dans la fenêtre client ; la fiche principale contient des
fiches enfant. Affectez la propriété FormStyle de l’objet TForm pour spécifier si la
fiche est un enfant (fsMDIForm) ou si c’est la fiche principale (fsMDIChild). Pour
éviter d’avoir à redéfinir à plusieurs reprises les propriétés des fenêtres enfant,
vous avez intérêt à définir une classe de base pour les fiches enfant et à dériver
chaque fiche enfant de cette classe.

Définition des options de l’EDI, du projet et de la compilation


Utilisez la commande Projet|Options du projet pour spécifier diverses options de
votre projet. Pour plus d’informations, voir l’aide en ligne.

4-2 Guide du développeur


Création d’applications

Définition des options de projet par défaut


Pour modifier les options de projet par défaut qui s’appliquent à tout nouveau
projet, définissez les options de la boîte de dialogue Options de projet, puis
cochez la case Défaut en bas à droite de la fenêtre. Tous les nouveaux projets
utiliseront ensuite les options en cours comme options par défaut.

Modèles de programmation
Les modèles de programmation sont des structures communément appelées
"squelettes" que vous pouvez ajouter au code source puis remplir. Par exemple,
si vous voulez utiliser une boucle for dans votre code, vous pouvez insérer le
modèle suivant :
for := to do
begin

end;
Pour insérer un modèle de code dans l’éditeur de code, appuyez sur Ctrl-j et
sélectionnez le modèle que vous voulez utiliser. Vous pouvez également ajouter
vos propres modèles à cette collection. Pour ajouter un modèle :
1 Sélectionnez Outils|Options d’environnement.
2 Choisissez l’onglet Audit de code.
3 Dans la section Modèles, choisissez Ajouter.
4 Saisissez un nom court et entrez une description brève du nouveau modèle.
5 Spécifiez le modèle de code dans la boîte de saisie Code.
6 Choisissez OK.

Applications console
Les applications console sont des programmes 32 bits exécutés sans interface
graphique, généralement dans une fenêtre console. Habituellement, ces
applications ne nécessitent pas une saisie utilisateur importante et exécutent un
jeu limité de fonctions.
Pour créer une nouvelle application console, choisissez Fichier|Nouveau et
sélectionnez Expert console dans la boîte de dialogue Nouveaux éléments.

Applications service
Les applications service reçoivent les requêtes des applications client, traitent ces
requêtes et renvoient les informations aux applications client. Habituellement,
elles s'exécutent en arrière-plan, sans nécessiter de saisie utilisateur importante.
Un serveur Web, FTP ou de messagerie électronique est un exemple
d'application service.

Création d’applications, de composants et de bibliothèques 4-3


Création d’applications

Pour créer une application qui implémente un service Win32, choisissez Fichier|
Nouveau puis sélectionnez Application service dans la boîte de dialogue
Nouveaux éléments. Cela ajoute à votre projet une variable globale appelée
Application de type TServiceApplication.
Une fois une application service créée, une fenêtre apparaît dans le concepteur
qui correspond à un service (TService). Implémentez le service en initialisant ses
propriétés et ses gestionnaires d’événements dans l’inspecteur d’objets. Vous
pouvez ajouter des services supplémentaires en choisissant Service dans la boîte
de dialogue Nouveaux éléments. N’ajoutez pas de services à une application qui
n’est pas une application service. En effet, même si un objet TService est ajouté,
l’application ne génère pas les événements nécessaires, ni ne fait les appels
Windows appropriés au service.
Une fois que votre application service est construite, vous pouvez installer ses
services avec le SCM (Service Control Manager). Les autres applications peuvent
alors lancer vos services en envoyant des requêtes au SCM.
Pour installer les services de votre application, exécutez-la à l'aide de l'option
/INSTALL. L'application installe ses services puis quitte, en affichant un message
de confirmation si les services sont correctement installés. Vous pouvez
supprimer l’affichage du message de confirmation en exécutant l'application
service à l'aide de l'option /SILENT.
Pour désinstaller les services, exécutez-la depuis la ligne de commande à l'aide
de l'option /UNINTALL (vous pouvez aussi utiliser l'option /SILENT pour
supprimer l'affichage du message de confirmation lors de la désinstallation).
Exemple Le service suivant contient un TServerSocket dont le port est initialisé à 80. C’est
le port par défaut des navigateurs Web pour envoyer des requêtes à des
serveurs Web et celui utilisé par les serveurs Web pour répondre aux
navigateurs Web. Cet exemple spécifique produit, dans le répertoire C:\Temp,
un document texte appelé WebLogxxx.log (où xxx correspond au ThreadID). Il
ne doit y avoir qu’un seul serveur surveillant un port donné, donc si vous
utilisez déjà un serveur Web, vous devez vous assurer qu’il n’est pas à l’écoute
(le service doit être arrêté).
Pour voir les résultats, ouvrez un navigateur Web sur la machine locale. Pour
l’adresse, entrez “localhost” (sans les guillemets). Eventuellement, le navigateur
va faire une erreur de dépassement de délai mais vous devez obtenir un fichier
appelé weblogxxx.log dans le répertoire C:\temp.
1 Pour créer l’exemple, choisissez Fichier|Nouveau et sélectionnez Application
Service dans la boîte de dialogue Nouveaux éléments. Une fenêtre nommée
Service1 apparaît. Ajoutez un composant ServerSocket de la page Internet de
la palette de composants à la fenêtre service (Service1).
2 Ajoutez ensuite une donnée membre de type TMemoryStream à la classe
TService1. La section interface de l’unité doit ressembler à :
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs,
ScktComp;

4-4 Guide du développeur


Création d’applications

type
TService1 = class(TService)
ServerSocket1: TServerSocket;
procedure ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
procedure Service1Execute(Sender: TService);
private
{ déclarations privées }
Stream: TMemoryStream; // Ajoutez cette ligne
public
function GetServiceController: PServiceController; override ;
{ déclarations publiques }
end;
var
Service1: TService1;
3 Sélectionnez ensuite ServerSocket1, le composant ajouté à l’étape 1. Dans
l’inspecteur d’objets, double-cliquez sur l’événement OnClientRead et ajoutez
le gestionnaire d’événement suivant :
procedure TService1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
Buffer: PChar;
begin
Buffer := nil;
while Socket.ReceiveLength > 0 do begin
try
Buffer := AllocMem(Socket.ReceiveLength);
Socket.ReceiveBuf(Buffer^, Socket.ReceiveLength);
Stream.Write(Buffer^, StrLen(Buffer));
finally
FreeMem(Buffer);
end;
Stream.Seek(0, soFromBeginning);
Stream.SaveToFile('c:\Temp\Weblog' + IntToStr(ServiceThread.ThreadID) + '.log');
end;
end;
4 Sélectionnez enfin Service1 en cliquant sur la zone client de la fenêtre (mais
pas sur le composant ServiceSocket). Dans l’inspecteur d’objets, double-cliquez
sur l’événement OnExecute et ajoutez le gestionnaire d’événement suivant :
procedure TService1.Service1Execute(Sender: TService);
begin
Stream := TMemoryStream.Create;
try
ServerSocket1.Port := 80; // port WWW
ServerSocket1.Active := True;
while not Terminated do begin
ServiceThread.ProcessRequests(False);
end;

Création d’applications, de composants et de bibliothèques 4-5


Création d’applications

ServerSocket1.Active := False;
finally
Stream.Free;
end;
end;
Quand vous écrivez votre application service, vous devez tenir compte des
éléments suivants :
• Threads de service
• Propriétés de nom d’un service
• Débogage des services

Threads de service
Chaque service dispose de son propre thread (TServiceThread), donc si votre
application service implémente plusieurs services, vous devez vous assurer que
l’implémentation de vos services est compatible avec l’utilisation de threads. La
classe TServiceThread est ainsi conçue de façon à implémenter le service dans le
gestionnaire d’événement OnExecute de TService. Le thread du service dispose de
sa propre méthode Execute qui contient une boucle appelant les gestionnaires
OnStart et OnExecute du service avant de traiter de nouvelles requêtes. Comme le
traitement des requêtes de service peut prendre longtemps et l’application
service peut recevoir simultanément plusieurs requêtes d’un ou de plusieurs
clients, il est plus efficace de lancer un nouveau thread (dérivé de TThread et non
de TServiceThread) pour chaque requête et de déplacer l’implémentation du
service dans la méthode Execute du nouveau thread. Cela permet à la boucle
Execute du thread du service de traiter continuellement de nouvelles requêtes
sans avoir à attendre la fin du gestionnaire OnExecute du service. Cette manière
de procéder est illustrée par l’exemple suivant :
Exemple Ce service sonne tous les 500 millisecondes depuis le thread standard. Il gère la
pause, la reprise et l’arrêt du thread quand on indique au service de se
suspendre, de reprendre ou de s’arrêter.
1 Choisissez Fichier|Nouveau et sélectionnez Application Service dans la boîte
de de dialogue Nouveaux éléments. Une fenêtre nommée Service1 apparaît.
2 Dans la section interface de votre unité, déclarez un nouveau descendant de
TThread nommé TSparkyThread. C’est le thread qui réalise le travail pour le
service. Il doit être déclaré comme suit :
TSparkyThread = class(TThread)
public
procedure Execute; override ;
end;
3 Ensuite, dans la section implementation de l’unité, créez une variable globale
pour une instance de TSparkyThread :
var
SparkyThread: TSparkyThread;

4-6 Guide du développeur


Création d’applications

4 Ajoutez le code suivant à la section implementation pour la méthode Execute


de TSparkyThread (la fonction thread) :
procedure TSparkyThread.Execute;
begin
while not Terminated do
begin
Beep;
Sleep(500);
end;
end;
5 Sélectionnez la fenêtre service (Service1) et double-cliquez sur l’événement
OnStart dans l’inspecteur d’objets. Ajoutez le gestionnaire d’événement
OnStart suivant :
procedure TService1.Service1Start(Sender: TService; var Started: Boolean);
begin
SparkyThread := TSparkyThread.Create(False);
Started := True;
end;
6 Double-cliquez sur l’événement OnContinue dans l’inspecteur d’objets. Ajoutez
le gestionnaire d’événement OnContinue suivant :
procedure TService1.Service1Continue(Sender: TService; var Continued: Boolean);
begin
SparkyThread.Resume;
Continued := True;
end;
7 Double-cliquez sur l’événement OnPause dans l’inspecteur d’objets. Ajoutez le
gestionnaire d’événement OnPause suivant :
procedure TService1.Service1Pause(Sender: TService; var Paused: Boolean);
begin
SparkyThread.Suspend;
Paused := True;
end;
8 Enfin, double-cliquez sur l’événement OnStop dans l’inspecteur d’objets.
Ajoutez le gestionnaire d’événement OnStop suivant :
procedure TService1.Service1Stop(Sender: TService; var Stopped: Boolean);
begin
SparkyThread.Terminate;
Stopped := True;
end;
Dans le cadre du développement d’applications serveur, la décision de lancer un
nouveau thread dépend de la nature du service rendu, du nombre prévu de
connexions et du nombre prévu de processeurs dont dispose la machine
exécutant le service.

Création d’applications, de composants et de bibliothèques 4-7


Création d’applications

Propriétés de nom d’un service


La VCL propose des classes permettant de créer des applications service, entre
autres, TService et TDependency. Quand vous utilisez ces classes, les diverses
propriétés de nom peuvent être source de confusion. Cette section décrit leurs
différences.
Les services ont des noms d’utilisateur (appelés Nom de démarrage du service)
qui sont associés à des mots de passe, des noms d’affichage utilisés pour
l’affichage dans les fenêtres gestionnaire et éditeur et des noms réels (le nom du
service). Les dépendances peuvent être des services ou des groupes d’ordre de
chargement. Elles ont également des noms et des noms d’affichage. De plus,
comme les objets service dérivent de TComponent, ils héritent de la propriété
Name. Les paragraphes suivants décrivent ces diverses propriétés de nom.

Propriétés de nom de TDependency


La propriété DisplayName de TDependency est à la fois le nom d’affichage et le
nom réel du service. Elle est presque toujours identique à la propriété Name de
TDependency.

Propriétés de nom de TService


La propriété Name de TService est héritée de TComponent. C’est le nom du
composant et également le nom du service. Pour les dépendances qui sont des
services, cette propriété est identique aux propriétés Name de TDependency et
DisplayName de TDependency.
TService::DisplayName est le nom affiché dans la fenêtre du gestionnaire de
service. Il diffère souvent du nom réel du service (TService.Name,
TDependency.DisplayName, TDependency.Name). Remarquez que généralement le
nom d’affichage (DisplayName) n’est pas le même pour le service et pour la
dépendance.
Les noms de démarrage de service sont distincts du nom d’affichage et du nom
réel du service. Un ServiceStartName est la valeur saisie du nom d’utilisateur
dans la boîte de dialogue de démarrage sélectionnée depuis le gestionnaire de
contrôle de service.

Débogage des services


Le débogage des applications service peut être difficile car il nécessite des
intervalles de temps courts :
1 Lancez d’abord l’application dans le débogueur. Patientez quelques secondes
jusqu’à la fin du chargement.
2 Démarrez rapidement le service à partir du panneau de configuration ou de la
ligne de commande :
net start MyServ
Vous devez lancer le service rapidement (dans les 15 à 30 secondes du
démarrage de l’application) car l’application se terminera si aucun service n’est
lancé.

4-8 Guide du développeur


Création de paquets et de DLL

Une autre approche est l’attachement au processus d’application service quand il


est en cours d’exécution (c’est-à-dire le démarrage en premier du service, puis
l’attachement au débogueur). Pour effectuer l’attachement au processus
d’application service, choisissez Exécuter|Attacher au processus et sélectionnez
l’application service dans la boîte de dialogue résultante.
Dans certains cas, cette seconde approche peut échouer en raison de droits
insuffisants. Si cela se produit, vous pouvez utiliser le gestionnaire de contrôle
de service pour permettre à votre service de fonctionner avec le débogueur :
1 Créez une clé (options d’exécution du fichier image) dans l’emplacement de
registre suivant :
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion
2 Créez une sous-clé portant le même nom que votre service (par exemple,
MONSERV.EXE). Ajoutez à cette sous-clé une valeur de type REG_SZ, nommée
Debugger. Utilisez le chemin complet au débogueur comme valeur de chaîne.
3 Dans l’applet Services du panneau de configuration, sélectionnez votre service,
cliquez sur l’option de démarrage et activez l’option permettant au service
d’interagir avec le bureau.

Création de paquets et de DLL


Les bibliothèques de liaison dynamique (DLL) sont des modules de code
compilés qui fonctionnent en conjonction avec un exécutable pour proposer des
fonctionnalités à une application.
Les paquets sont des DLL spéciales utilisées par Delphi , par l’EDI ou les deux à
la fois. Il y a deux sortes de paquets : les paquets d’exécution et les paquets de
conception. Les paquets d’exécution fournissent des fonctionnalités à un
programme lors de son exécution. Les paquets de conception permettent
d’étendre les fonctionnalités de l’EDI.
Pour davantage d’informations sur les paquets, voir chapitre 9, “Utilisation des
paquets et des composants”.

Utilisation des paquets et des DLL


Pour la plupart des applications écrites avec Delphi, les paquets offrent une plus
grande flexibilité et sont plus simples à créer que les DLL. Dans certaines
situations, les DLL sont mieux adaptées à vos projets que des paquets :
• Votre module de code doit être appelé par une application qui n’a pas été
conçue avec Delphi .
• Vous étendez les fonctionnalités d’un serveur Web.
• Vous créez un module de code qui doit être utilisé par des développeurs
extérieurs.
• Votre projet est un conteneur OLE.

Création d’applications, de composants et de bibliothèques 4-9


Ecriture d'applications de base de données

Ecriture d'applications de base de données


Un des atouts de Delphi est sa possibilité de créer des applications de base de
données sophistiquées. Delphi fournit des outils intégrés permettant de vous
connecter à Oracle, Sybase, Informix, dBASE, Paradox ou à d'autres serveurs,
tout en assurant un partage des données transparent entre les différentes
applications. Le moteur de base de données Borland (BDE) supporte l'évolution
des applications de bureau vers le client/serveur.
Les outils, tels l'explorateur de bases de données, simplifie l'écriture des
applications de base de données. L'explorateur de bases de données est un
scruteur arborescent, servant à inspecter et à modifier les objets de schémas
spécifiques aux serveurs de bases de données (tables, champs, définitions de
procédures stockées, déclencheurs, références et descriptions d'index).
Après connexion à une base de données, l'explorateur de bases de données vous
permet de :
• Créer et maintenir les alias de la base
• Voir les informations de schéma de la base de données, telles que les tables,
les procédures stockées et les déclencheurs
• Voir les objets des tables, tels que les champs et les index
• Créer, voir et modifier les données dans les tables
• Entrer des instructions SQL pour interroger directement n'importe quelle base
de données
• Créer et maintenir des dictionnaires de données pour stocker les ensembles
d'attributs
Pour des détails sur l’utilisation de Delphi pour créer des applications base de
données client ou serveur, voir partie II, “Développement d’applications de base
de données,” de ce manuel.

Conception d’applications distribuées


Les applications distribuées sont des applications déployées sur diverses
machines et plates-formes qui fonctionnent ensemble, généralement via un
réseau, pour effectuer un ensemble d’opérations liées les unes aux autres. Par
exemple, une application permettant d’acheter des articles et de suivre ces achats
pour une société couvrant le pays entier implique des applications client
individuelles, un serveur principal qui traite les requêtes des clients et une
interface avec une base de données stockant toutes les informations sur ces
transactions. En concevant une application client distribuée, par exemple une
application utilisant le Web, la maintenance et la mise à jours des clients est
grandement simplifiée.

4-10 Guide du développeur


Conception d’applications distribuées

Delphi propose plusieurs options pour le modèle d’implémentation des


applications distribuées :
• Applications TCP/IP
• Applications COM et DCOM
• Applications CORBA
• Applications de base de données

Distribution d’applications en utilisant TCP / IP


TCP/IP est un protocole de communication qui vous permet d’écrire des
applications communiquant au travers de réseaux. Vous pouvez implémenter
virtuellement toutes les conceptions possibles dans votre application. En effet,
TCP/IP définit une couche de transport mais n’impose aucune architecture
particulière pour la création de l’application distribuée.
La croissance d’Internet a créé un environnement dans lequel la plupart des
ordinateurs disposent déjà d’une forme d’accès TCP/IP, ce qui simplifie
considérablement la distribution et la configuration de l’application.
Les applications utilisant TCP/IP peuvent être des applications distribuées à base
de message (comme une application serveur Web qui traite des messages de
requête HTTP) ou des applications à objet distribué (comme des applications de
base de données distribuée qui communiquent en utilisant les sockets Windows).
Le moyen le plus simple d’ajouter des fonctionnalités TCP/IP à votre application
est d’utiliser les sockets client ou serveur. Delphi gère également les applications
qui étendent les serveurs Web en créant des scripts CGI ou des DLL. De plus,
Delphi gère également les applications de base de données utilisant TCP/IP.

Utilisation de sockets dans les applications


Deux classes de la VCL, TClientSocket et TServerSocket, vous permettent de créer
des connexions de socket TCP/IP afin de communiquer avec d’autres
applications distantes. Pour davantage d’informations sur les sockets, voir
chapitre 30, “Utilisation des sockets”

Création d’applications serveur Web


Pour créer une nouvelle application serveur Web, sélectionnez Fichier|Nouveau
et Application serveur Web dans la boîte de dialogue Nouveaux éléments.
Choisissez ensuite le type d’application serveur Web :
• ISAPI et NSAPI
• CGI autonome
• Win-CGI autonome
Les applications CGI et Win-CGI utilisent davantage de ressources système sur le
serveur, les applications complexes sont mieux gérées si elles sont créées sous la
forme d’applications ISAPI ou NSAPI.
Pour davantage d’informations sur la création d’applications serveur Web, voir
chapitre 29, “Création d’applications serveur pour Internet”.

Création d’applications, de composants et de bibliothèques 4-11


Conception d’applications distribuées

Applications serveur Web ISAPI et NSAPI


La sélection de ce type d’application configure votre projet comme une DLL. Les
applications serveur Web ISAPI ou NSAPI sont des DLL chargées par le serveur
Web. Les informations sont transmises à la DLL, traitées puis renvoyées au client
par le serveur Web.

Applications serveur Web autonomes CGI


Les applications serveur Web CGI sont des applications console qui reçoivent les
requêtes des clients sur l’entrée standard, traitent ces requêtes et renvoient le
résultat sur la sortie standard afin qu’il soit renvoyé au client.

Applications serveur Web autonomes Win-CGI


Les applications serveur Web Win-CGI sont des applications Windows qui
reçoivent les requêtes des clients à partir d’un fichier INI écrit par le serveur et
qui écrivent le résultat dans un fichier que le serveur envoie au client.

Distribution d’applications en utilisant COM et DCOM


COM (Component Object Model) propose une architecture d’objet distribué sous
Windows conçue pour assurer une interopérabilité des objets en utilisant des
routines prédéfinies appelées des interfaces. Les applications COM utilisent des
objets implémentés par un processus différent ou, si vous utilisez DCOM, sur
une machine différente.

COM et DCOM
Delphi contient des classes et des experts qui simplifient la création des éléments
essentiels d’une application COM, OLE ou ActiveX. L’utilisation de Delphi pour
créer des applications basées sur COM offre de nombreuses possibilités, allant de
l’amélioration de la conception de logiciel en utilisant des interfaces de manière
interne dans une application, à la création d’objets qui peuvent interagir avec
d’autres objets utilisant l’API COM du système, comme les extensions du shell
Win95 ou la gestion multimedia DirectX.
Pour davantage d’informations sur COM et les contrôles ActiveX, voir
chapitre 44, “Présentation des technologies COM”, chapitre 48, “Création d’un
contrôle ActiveX” et “Distribution d’une application client en tant que contrôle
ActiveX” à la page 14-33.
Pour davantage d’informations sur DCOM, voir “Utilisation de connexions
DCOM” à la page 14-10.

MTS
Le serveur de transaction Microsoft (MTS) est une extension de COM qui met à
la disposition des applications distribuées COM, des services de transaction, la
sécurité et la mise en commun des ressources.
Pour davantage d’informations sur MTS, voir chapitre 50, “Création des objets
MTS” et “Utilisation de MTS” à la page 14-6.

4-12 Guide du développeur


Conception d’applications distribuées

Distribution d’applications en utilisant CORBA


CORBA (Common Object Request Broker Architecture) est une méthode
d’utilisation d’objets distribués dans des applications. Le standard CORBA est
utilisé sur plusieurs plates-formes, l’écriture d’applications CORBA vous permet
donc d’accéder à des programmes ne fonctionnant pas sur une machine
Windows.
A l’image de COM, CORBA est une architecture d’objet distribué, cela signifie
que les applications client peuvent utiliser des objets implémentés dans un
serveur distant.
Pour plus d’informations sur CORBA, voir chapitre 28, “Ecriture d’applications
CORBA”.
Pour obtenir des instructions sur la distribution d’applications en utilisant
CORBA, voir “Déploiement d’applications CORBA” à la page 28-18.

Distribution d’applications de base de données


Delphi permet de créer des applications de base de données distribuées en
utilisant la technologie MIDAS. Cette technologie puissante propose un ensemble
de composants associés qui vous permettent de concevoir une large gamme
d’applications de base de données multiniveaux. Il est ainsi possible d’écrire des
applications de base de données en utilisant divers protocoles de communication,
dont DCOM, CORBA, TCP/IP et OLEnterprise.
Pour davantage d’informations sur la conception d’applications de base de
données distribuées, voir chapitre 14, “Création d’applications multiniveaux”.
Le déploiement d’applications de base de données nécessite souvent le
déploiement du moteur de bases de données Borland (BDE) en plus des fichiers
de l’application. Pour des informations sur le déploiement du BDE, voir
“Déploiement d’applications de base de données” à la page 11-4.

Création d’applications, de composants et de bibliothèques 4-13


4-14 Guide du développeur
Chapitre

Conception de l’interface
Chapter 5
5
utilisateur des applications
Delphi vous permet de créer une interface utilisateur en sélectionnant des
composants de la palette des composants et en les déposant dans des fiches.

TApplication, TScreen et TForm


TApplication, TScreen et TForm sont les classes VCL qui constituent la base de
toutes les applications Delphi en contrôlant le comportement de votre projet. La
classe TApplication sert de fondation à une application Windows en fournissant
les propriétés et les méthodes qui encapsulent le comportement d’un programme
Windows standard. La classe TScreen est utilisée à l’exécution pour gérer les
fiches et les modules de données chargés, ainsi que des informations spécifiques
comme la résolution écran ou les fontes utilisables à l’affichage. Des instances de
la classe TForm servent à construire l’interface utilisateur de votre application.
Les fenêtres et les boîtes de dialogue d’une application sont basées sur TForm.

Utilisation de la fiche principale


TForm est la classe essentielle dans la création d’applications Windows disposant
d’une interface utilisateur graphique.
La première fiche créée et enregistrée dans un projet devient, par défaut, la fiche
principale du projet : c’est la première fiche créée à l’exécution. Quand vous
ajoutez d’autres fiches dans vos projets, vous pouvez en choisir une autre pour
servir de fiche principale à votre application. Par ailleurs, faire d’une fiche la
fiche principale est le moyen le plus simple de la tester à l’exécution : à moins de
changer explicitement l’ordre de création, la fiche principale est la première fiche
affichée lors de l’exécution d’une application.

Conception de l’interface utilisateur des applications 5-1


TApplication, TScreen et TForm

Pour changer la fiche principale d’un projet :


1 Choisissez Projet|Options et sélectionnez la page Fiche.
2 Dans la boîte liste Fiche principale, sélectionnez la fiche à utiliser comme fiche
principale du projet et choisissez OK.
Exécutez votre application, la nouvelle fiche principale est affichée.

Ajout de fiches supplémentaires


Pour ajouter une fiche supplémentaire à votre projet, sélectionnez Fichier|
Nouvelle fiche. Toutes les fiches d’un projet ainsi que les unités correspondantes
sont affichées dans le gestionnaire de projet (Voir|Gestionnaire de projet).

Liaison de fiches
L’ajout d’une fiche au projet ajoute au fichier projet une référence à cette fiche
mais pas aux autres unités du projet. Avant d’écrire du code faisant référence à
la nouvelle fiche, vous devez ajouter une référence à cette fiche dans les fichiers
unité des fiches y faisant référence. Cela s’appelle la liaison de fiche.
La liaison de fiche est fréquemment utilisée pour donner accès aux composants
contenus dans une autre fiche. Par exemple, la liaison de fiche est souvent
employée pour permettre à une fiche contenant des composants orientés données
de se connecter aux composants d’accès aux données d’un module de données.
Pour lier une fiche à une autre fiche :
1 Sélectionnez la fiche qui fait référence à une autre.
2 Choisissez Fichier|Utiliser unité.
3 Sélectionnez le nom de l’unité de la fiche qui doit être référencée.
4 Choisissez OK.
Lier une fiche à une autre signifie simplement que les clauses uses de l’unité
d’une fiche contient une référence àl’unité de l’autre fiche. Ainsi la fiche liée et
ses composants rentrent dans la portée de la fiche.

Références circulaires d’unités


Quand deux fiches doivent se référencer mutuellement, il est possible de générer
une erreur “Référence circulaire” lors de la compilation du programme. Pour
éviter une telle erreur, utilisez l’une des méthodes suivantes :
• Placez les deux clauses uses, avec les identificateurs d’unités, dans la section
implementation de leur fichier unité respectif (action de Fichier|Utiliser
unité).
• Placez l’une des clauses uses dans la section interface et l’autre dans la
section implementation. (il est rarement nécessaire de placer l’identificateur de
l’unité d’une autre fiche dans la section interface).
Ne placez pas les deux clauses uses dans la section interface de leur fichier
unité respectif. Cela provoque l’erreur “Référence circulaire” à la compilation.

5-2 Guide du développeur


TApplication, TScreen et TForm

Manipulation de l’application
La variable globale Application de type TApplication se trouve dans chaque
application Windows Delphi. Application encapsule l’application et propose de
nombreux services fonctionnant en arrière-plan du programme. Ainsi, Application
gère la manière d’appeler un fichier d’aide depuis les menus de votre
programme. La compréhension du fonctionnement de TApplication est plus
importante pour le concepteur de composants que pour le développeur
d’applications autonomes, mais vous devez définir les options gérées par
Application dans la page Application de la boîte de dialogue Options de projet
(Projet|Options) quand vous créez un projet.
De plus, Application reçoit de nombreux événements qui s’appliquent à
l’application dans son ensemble. Par exemple, l’événement OnActivate vous
permet de réaliser des actions au démarrage de l’application, l’événement OnIdle
vous permet d’exécuter des traitements en arrière-plan lorsque l’application n’est
pas occupée, l’événement OnMessage vous permet d’intercepter les messages
Windows, etc. Bien que vous ne puissiez pas utiliser l’EDI pour examiner les
propriétés et les événements de la variable globale Application, un autre
composant, TApplicationEvents, intercepte les événements et vous permet de
fournir les gestionnaires d’événements à l’aide de l’EDI.

Gestion de l’écran
Une variable globale de type TScreen, appelée Screen, est créée lors de la création
d’un projet. Screen encapsule l’état de l’écran dans lequel l’application s’exécute.
Parmi les fonctions imparties à Screen, il y a la gestion de l’aspect du curseur, la
taille de la fenêtre dans laquelle s’exécute l’application, la liste des fontes
disponibles pour le périphérique écran et d’autres comportements de l’écran. Si
votre application s’exécute sur plusieurs moniteurs, Screen gère une liste des
moniteurs et leurs dimensions afin que vous puissiez effectivement gérer la
disposition de l’interface utilisateur.

Gestion de la disposition
A son niveau le plus élémentaire, vous contrôlez l’organisation de votre interface
utilisateur par la manière de disposer les contrôles dans les fiches. Le choix des
emplacements est reflété par les propriétés Top, Left, Width et Height des
contrôles. Vous pouvez modifier ces valeurs à l’exécution afin de modifier la
position ou la taille des contrôles dans les fiches.
Les contrôles disposent de nombreuses autres propriétés qui leur permettent de
s’adapter automatiquement à leur contenu ou à leur conteneur. Cela vous permet
d’organiser les fiches de telle manière que les différents éléments forment un
tout unifié.

Conception de l’interface utilisateur des applications 5-3


Utilisation des messages

Deux propriétés contrôlent la position et la taille d’un contrôle relativement à


celle de son parent. La propriété Align vous permet d’obliger un contrôle à
s’adapter exactement à un côté spécifié de son parent ou à occuper toute la place
disponible de la zone client du parent une fois les autres contrôles alignés.
Quand le parent est redimensionné, les contrôles alignés sont automatiquement
redimensionnés et restent positionnés le long d’un côté donné du parent.
Si vous voulez qu’un contrôle reste positionné relativement à un côté particulier
de son parent sans toucher ce bord ou être redimensionné pour occuper la
totalité du côté, vous pouvez utiliser la propriété Anchors.
Pour vous assurer qu’un contrôle ne devient ni trop grand ni trop petit, vous
pouvez utiliser la propriété Constraints. Constraints vous permet de spécifier la
hauteur maximum, la hauteur minimum, la largeur maximum et la largeur
minimum du contrôle. Initialisez ces valeurs afin de limiter la taille (en pixels)
de la hauteur et de la largeur du contrôle. Ainsi, en initialisant les contraintes
MinWidth et MinHeight d’un objet conteneur, vous êtes certain que ses objets
enfant sont toujours visibles.
La valeur de Constraints se propage le long de la hiérarchie parent/enfant de
telle manière que la taille d’un objet peut être restreinte car il contient des
enfants alignés qui ont des contraintes de taille. Constraints peut également
empêcher un contrôle d’être mis à l’échelle dans une dimension particulière lors
de l’appel de sa méthode ChangeScale.
TControl introduit un événement protégé, OnConstrainedResize, de type
TConstrainedResizeEvent :
TConstrainedResizeEvent = procedure (Sender: TObject; var MinWidth, MinHeight, MaxWidth,
MaxHeight: Integer) of object;
Cet événement vous permet de surcharger les contraintes de taille lors d’une
tentative de redimensionnement du contrôle. Les valeurs des contraintes sont
transmises comme paramètres var, elles peuvent donc être modifiées dans le
gestionnaire d’événement. OnConstrainedResize est publié pour les objets
conteneur (TForm, TScrollBox, TControlBar et TPanel). De plus, les concepteurs de
composants peuvent utiliser ou publier cet événement dans tout descendant de
TControl.
Les contrôles dont le contenu peut changer de taille ont une propriété AutoSize
qui force le contrôle à adapter sa taille à l’objet ou à la fonte qu’il contient.

Utilisation des messages


Un message est une notification envoyée par Windows à une application
l’informant qu’un événement s’est produit. Le message lui-même est un
enregistrement transmis par Windows à un contrôle. Par exemple, quand vous
cliquez sur un bouton de la souris dans une boîte de dialogue, Windows envoie
au contrôle actif un message et l’application contenant ce contrôle réagit à ce
nouvel événement. Si vous avez cliqué sur un bouton, l’événement OnClick peut

5-4 Guide du développeur


Informations supplémentaires sur les fiches

être activé à la réception du message. Si vous avez juste cliqué dans la fiche,
l’application peut ne pas tenir compte du message.
Le type enregistrement transmis par Windows à l’application est appelé un
TMsg. Windows prédéfinit une constante pour chaque message, ces valeurs sont
stockées dans le champ de message de l’enregistrement TMsg. Chacune de ces
constantes commence par les lettres “wm”.
La VCL gère automatiquement les messages sauf si vous surchargez le système
de gestion des messages et créez vos propres gestionnaires d’événements. Pour
davantage d’informations sur les messages et la gestion des messages, voir
“Compréhension du système de gestion des messages” à la page 37-1,
“Modification de la gestion des messages” à la page 37-3 et “Création de
nouveaux gestionnaires de messages” à la page 37-5.

Informations supplémentaires sur les fiches


Quand vous créez une fiche Delphi dans l’EDI, Delphi crée automatiquement la
fiche en mémoire en ajoutant du code dans la fonction WinMain(). C’est
généralement le comportement souhaité et vous n’avez donc rien à y changer.
Ainsi, la fiche principale existe pour toute la durée du programme, il est donc
peu probable que vous changiez le comportement par défaut de Delphi quand
vous créez la fiche de votre fenêtre principale.
Néanmoins, il n’est pas toujours nécessaire de conserver en mémoire toutes les
fiches de votre application pour toute la durée de l’exécution du programme. Si
vous ne souhaitez pas avoir tout le temps en mémoire toutes les boîtes de
dialogue de votre application, vous pouvez les créer dynamiquement quand vous
voulez les voir apparaître.
Une fiche peut être modale ou non modale. Les fiches modales sont des fiches
avec lesquelles l’utilisateur doit interagir avant de pouvoir passer à une autre
fiche (par exemple une boîte de dialogue impose une saisie de l’utilisateur). Les
fiches non modales sont des fenêtres visibles tant qu’elles ne sont pas masquées
par une autre fenêtre, fermées ou réduites par l’utilisateur.

Contrôle du stockage en mémoire des fiches


Par défaut, Delphi crée automatiquement en mémoire la fiche principale de
l’application en ajoutant le code suivant dans l’unité source du projet :
Application.CreateForm(TForm1, Form1);
Cette fonction crée une variable globale portant le même nom que la fiche. Ainsi,
chaque fiche d’une application a une variable globale associée. Cette variable est
un pointeur sur une instance de la classe de la fiche et sert à désigner la fiche
durant l’exécution de l’application. Toute unité qui inclut l’unité de la fiche dans
sa clause uses peut accéder à la fiche par l’intermédiaire de cette variable.

Conception de l’interface utilisateur des applications 5-5


Informations supplémentaires sur les fiches

Toutes les fiches créées de cette manière dans l’unité du projet apparaissent
quand le programme est exécuté et restent en mémoire durant toute l’exécution
de l’application.

Affichage d’une fiche créée automatiquement


Il est possible de créer une fiche au démarrage, mais de ne l’afficher que plus
tard dans l’exécution du programme. Les gestionnaires d’événements de la fiche
utilisent la méthode ShowModal pour afficher une fiche déjà chargée en mémoire :
procedure TMainForm.Button1Click(Sender: TObject);
begin
ResultsForm.ShowModal;
end;
Dans ce cas, comme la fiche est déjà en mémoire, il n’est pas nécessaire de créer
une autre instance ou de détruire cette instance.

Création dynamique de fiche


Toutes les fiches de votre application n’ont pas besoin d’être en mémoire
simultanément. Pour réduire la quantité de mémoire nécessaire au chargement
de l’application, vous pouvez créer certaines fiches uniquement quand vous en
avez besoin. Ainsi, une boîte de dialogue n’a besoin d’être en mémoire que
pendant le temps où l’utilisateur interagit avec.
Pour spécifier dans l’EDI que la fiche doit être créée à un autre moment pendant
l’exécution, procédez de la manière suivante :
1 Sélectionnez Fichier|Nouvelle fiche afin d’afficher la nouvelle fiche.
2 Retirez la fiche de la liste Fiches créées automatiquement dans la page Fiches
de la boîte de dialogue Options de projet.
Cela retire l’appel de la fiche au démarrage. Vous pouvez également retirer
manuellement la ligne suivante dans le source du projet:
Application.CreateForm(TResultsForm, ResultsForm);
3 Appelez la fiche au moment souhaité en utilisant la méthode Show de la fiche
si la fiche est non modale ou la méthode ShowModal si la fiche est modale.
Un gestionnaire d’événement de la fiche principale doit créer et détruire une
instance de la fiche appelée ResultsForm dans l’exemple précédent. Une manière
d’appeler cette fiche consiste à utiliser la variable globale comme dans le code
suivant. Remarquez que ResultsForm étant une fiche modale, le gestionnaire
utilise la méthode ShowModal :
procedure TMainForm.Button1Click(Sender: TObject);
begin
ResultsForm:=TResultForm.Create(self)
ResultsForm.ShowModal;
ResultsForm.Free;
end;
Dans cet exemple, le gestionnaire d’événement supprime la fiche après sa
fermeture, la fiche doit donc être recréée si vous avez besoin de ResultsForm

5-6 Guide du développeur


Informations supplémentaires sur les fiches

ailleurs dans l’application. Si la fiche était affichée en utilisant Show, vous ne


pourriez la supprimer dans le gestionnaire d’événement car, après l’appel de
Show, l’exécution du code du gestionnaire se poursuit alors que la fiche est
toujours ouverte.
Remarque Si vous créez une fiche en utilisant son constructeur, assurez-vous que la fiche
n’apparaît pas dans la liste Fiches créées automatiquement de la page Fiches de
la boîte de dialogue Options de projet. En effet, si vous créez une nouvelle fiche
sans avoir détruit la fiche de même nom dans la liste, Delphi crée la fiche au
démarrage et le gestionnaire d’événement crée une nouvelle instance de la fiche,
ce qui remplace la référence à l’instance auto-créée. L’instance auto-créée existe
toujours mais l’application n’y a plus accès. A la fin du gestionnaire
d’événement, la variable globale ne pointe plus sur une fiche valide. Toute
tentative d’utiliser la variable globale entraînera probablement le blocage de
l’application.

Création de fiches non modales comme fenêtres


Vous devez vous assurer que les variables désignant des fiches non modales
existent tant que la fiche est utilisée. Cela signifie que ces variables doivent avoir
une portée globale. Le plus souvent, vous utiliserez la variable globale
référençant la fiche qui a été créée quand vous avez ajouté la fiche (le nom de
variable qui correspond à la valeur de la propriété Name de la fiche). Si votre
application a besoin d’autres instances de la fiche, déclarez des variables globales
distinctes pour chaque instance.

Utilisation d’une variable locale pour créer une instance de


fiche
Un moyen fiable de créer une seule instance d’une fiche modale consiste à utiliser
une variable locale du gestionnaire d’événement comme référence à la nouvelle
instance. Si une variable locale est employée, il importe peu que ResultsForm soit
ou non auto-créée. Le code du gestionnaire d’événement ne fait pas référence à
la variable fiche globale. Par exemple :
procedure TMainForm.Button1Click(Sender: TObject);
var
RF:TResultForm;
begin
RF:=TResultForm.Create(self)
RF.ShowModal;
RF.Free;
end;
Remarquez que l’instance globale de la fiche n’est jamais utilisée dans cette
version du gestionnaire d’événement.
Habituellement, les applications utilisent les instances globales des fiches.
Cependant, si vous avez besoin d’une nouvelle instance d’une fiche modale alors
que vous utilisez cette fiche dans une portion réduite de votre application (par
exemple dans une seule fonction), une instance locale est normalement le moyen
le plus rapide et le plus fiable de manipuler la fiche.

Conception de l’interface utilisateur des applications 5-7


Informations supplémentaires sur les fiches

Bien entendu, vous ne pouvez pas utiliser de variables locales pour les fiches
non modales dans les gestionnaires d’événements car elles doivent avoir une
portée globale pour garantir que la fiche existe aussi longtemps qu’elle est
utilisée. Show rend la main dès que la fiche est ouverte, donc si vous utilisez une
variable locale, la variable locale sort de portée immédiatement.

Transfert de paramètres supplémentaires aux fiches


Généralement, vous créez les fiches de votre application en utilisant l’EDI.
Quand elle est créée ainsi, la fiche a un constructeur qui prend un argument,
Owner, qui désigne le propriétaire de la fiche créée (c’est-à-dire l’objet application
ou l’objet fiche appelant). Owner peut avoir la valeur nil.
Pour transmettre d’autres paramètres à la fiche, créez un constructeur différent et
instanciez la fiche en utilisant ce nouveau constructeur. La classe fiche exemple
suivante utilise un constructeur supplémentaire proposant le paramètre
supplémentaire whichButton. Il faut ajouter manuellement ce nouveau
constructeur à la fiche :
TResultsForm = class(TForm)
ResultsLabel: TLabel;
OKButton: TButton;
procedure OKButtonClick(Sender: TObject);
private
public
constructor CreateWithButton(whichButton: Integer; Owner: TComponent);
end;
Voici l’implémentation, créée manuellement, de ce constructeur qui attend le
paramètre supplémentaire whichButton. Ce constructeur utilise le paramètre
whichButton pour initialiser la propriété Caption d’un contrôle Label de la fiche.
constructor CreateWithButton(whichButton: Integer; Owner: TComponent);
begin
case whichButton of
1: ResultsLabel.Caption := 'Vous avez choisi le premier bouton.';
2: ResultsLabel.Caption := 'Vous avez choisi le second bouton.';
3: ResultsLabel.Caption := 'Vous avez choisi le troisième bouton.';
end;
end;
Quand vous créez une instance d’une fiche disposant de plusieurs constructeurs,
vous pouvez sélectionner le constructeur le mieux adapté à vos besoins. Par
exemple, le gestionnaire OnClick suivant d’un bouton de la fiche crée une
instance de TResultsForm en utilisant le paramètre supplémentaire :
procedure TMainForm.SecondButtonClick(Sender: TObject);
var
rf: TResultsForm;
begin
rf := TResultsForm.CreateWithButton(2, self);
rf.ShowModal;
rf.Free;
end;

5-8 Guide du développeur


Informations supplémentaires sur les fiches

Récupération de données des fiches


La plupart des applications réelles utilisent plusieurs fiches. Bien souvent, il est
nécessaire de transmettre des informations entre ces différentes fiches. Il est
possible de transmettre des informations à une fiche sous la forme de paramètres
du constructeur de la fiche destination ou en affectant des valeurs aux propriétés
de la fiche. La méthode à utiliser pour obtenir des informations d’une fiche
change selon que la fiche est ou non modale.

Récupération de données dans des fiches non modales


Il est facile d’extraire des informations de fiches non modales en appelant des
fonctions membre publiques de la fiche ou en interrogeant ses propriétés. Soit,
par exemple, une application contenant une fiche non modale appelée ColorForm
qui contient une boîte liste appelée ColorListBox contenant une liste de couleurs
(“Rouge”, “Vert”, “Bleu”, etc). Le nom de couleur sélectionné dans ColorListBox
est automatiquement stocké dans une propriété appelée CurrentColor à chaque
fois que l’utilisateur sélectionne une nouvelle couleur.
La fiche a la déclaration de classe suivante :
TColorForm = class(TForm)
ColorListBox:TListBox;
procedure ColorListBoxClick(Sender: TObject);
private
FColor:String;
public
property CurColor:String read FColor write FColor;
end;
Le gestionnaire d’événement OnClick de la boîte liste ColorListBoxClick initialise la
valeur de la propriété CurrentColor à chaque fois qu’un nouvel élément est
sélectionné. Le gestionnaire d’événement obtient la chaîne dans la boîte liste qui
contient le nom de couleur et l’affecte à CurrentColor. La propriété CurrentColor
utilise la fonction d’affectation, SetColor, pour stocker la valeur réelle de la
propriété dans la donnée membre privée FColor:
procedure TColorForm.ColorListBoxClick(Sender: TObject);
var
Index: Integer;
begin
Index := ColorListBox.ItemIndex;
if Index >= 0 then
CurrentColor := ColorListBox.Items[Index]
else
CurrentColor := '';
end;

Conception de l’interface utilisateur des applications 5-9


Informations supplémentaires sur les fiches

Supposons maintenant qu’une autre fiche de l’application, appelée ResultsForm, a


besoin de connaître la couleur actuellement sélectionnée dans ColorForm à chaque
fois qu’un bouton (nommé UpdateButton) de ResultsForm est choisi. Le
gestionnaire d’événement OnClick de UpdateButton doit avoir la forme suivante :
procedure TResultForm.UpdateButtonClick(Sender: TObject);
var
MainColor: String;
begin
if Assigned(ColorForm) then
begin
MainColor := ColorForm.CurrentColor;
{faire quelque chose avec la chaîne MainColor}
end;
end;
Le gestionnaire d’événement commence par vérifier que ColorForm existe en
utilisant la fonction Assigned. Ensuite, il obtient la valeur de la propriété
CurrentColor de ColorForm.
En procédant autrement, si ColorForm a une fonction publique appelée GetColor,
une autre fiche peut obtenir la couleur en cours sans utiliser la propriété
CurrentColor (par exemple, MainColor := ColorForm.GetColor;). En fait, rien
n’empêche l’autre fiche d’obtenir la couleur sélectionnée dans ColorForm en
examinant directement la valeur sélectionnée dans la boîte liste :
with ColorForm.ColorListBox do
MainColor := Items[ItemIndex];
Néanmoins, l’utilisation d’une propriété rend l’interface avec ColorForm très claire
et simple. Tout ce qu’une fiche a besoin de savoir sur ColorForm, c’est comment
récupérer la valeur de CurrentColor.

Récupération de données dans des fiches modales


Tout comme les fiches non modales, les fiches modales contiennent souvent des
informations nécessaires à d’autres fiches. Dans le cas de figure la plus classique,
une fiche A lance la fiche modale B. Lors de la fermeture de B, la fiche A a
besoin de savoir ce que l’utilisateur a fait dans la fiche B pour décider comment
poursuivre les traitements de la fiche A. Si la fiche B est toujours en mémoire, il
est possible de l’interroger via ses propriétés et ses fonctions membres tout
comme les fiches non modales de l’exemple précédent. Mais comment faire si la
fiche B est retirée de la mémoire une fois fermée ? Comme une fiche ne renvoie
pas explicitement de valeur, il est nécessaire de préserver les informations
importantes de la fiche avant de la détruire.
Pour illustrer cette manière de procéder, considérez une version modifiée de la
fiche ColorForm conçue comme une fiche modale. Sa classe est déclarée de la
manière suivante :
TColorForm = class(TForm)
ColorListBox:TListBox;
SelectButton: TButton;
CancelButton: TButton;
procedure CancelButtonClick(Sender: TObject);
procedure SelectButtonClick(Sender: TObject);

5-10 Guide du développeur


Informations supplémentaires sur les fiches

private
FColor: Pointer;
public
constructor CreateWithColor(Value: Pointer; Owner: TComponent);
end;
La fiche contient une boîte liste nommée ColorListBox contenant une liste de
noms de couleur. Quand il est choisi, le bouton nommé SelectButton mémorise le
nom de la couleur sélectionnée dans ColorListBox puis ferme la fiche.
CancelButton est un bouton qui ferme simplement la fiche.
Remarquez l’ajout à la déclaration de la classe d’un constructeur défini par
l’utilisateur qui attend un argument Pointer. Ce paramètre Pointer pointe sur une
chaîne gérée par la fiche qui déclenche ColorForm. Ce constructeur a
l’implémentation suivante :
constructor TColorForm(Value: Pointer; Owner: TComponent);
begin
FColor := Value;
String(FColor^) := '';
end;
Le constructeur enregistre le pointeur dans une donnée membre privée FColor et
initialise la chaîne avec une chaîne vide.
Remarque Pour utiliser ce constructeur défini par l’utilisateur, la fiche doit être créée
explicitement. Ce ne peut pas être une fiche auto-créée au démarrage de
l’application. Pour davantage d’informations, voir “Contrôle du stockage en
mémoire des fiches” à la page 5-5.
Dans l’application, l’utilisateur sélectionne une couleur dans la boîte liste puis
clique sur le bouton SelectButton pour enregistrer son choix et fermer la fiche. Le
gestionnaire d’événement OnClick du bouton SelectButton doit avoir la forme
suivante :
procedure TColorForm.SelectButtonClick(Sender: TObject);
begin
with ColorListBox do
if ItemIndex >= 0 then
String(FColor^) := ColorListBox.Items[ItemIndex];
end;
Close;
end;
Remarquez comment le gestionnaire d’événement stocke le nom de couleur
sélectionné dans la chaîne référencée par le pointeur qui a été transmise au
constructeur.
Pratiquement, pour utiliser ColorForm, la fiche appelante doit transmettre au
constructeur un pointeur sur une chaîne existante. Supposons par exemple que
ColorForm est instanciée par une fiche appelée ResultsForm en réponse au choix
d’un bouton de ResultsForm nommé UpdateButton. Le gestionnaire d’événement
de ce bouton doit avoir la forme suivante :
procedure TResultsForm.UpdateButtonClick(Sender: TObject);
var
MainColor: String;

Conception de l’interface utilisateur des applications 5-11


Réutilisation des composants et des groupes de composants

begin
GetColor(Addr(MainColor));
if MainColor <> '' then
{faire quelque chose avec la chaîne MainColor}
else
{faire autre chose car aucune couleur n’a été sélectionnée}
end;

procedure GetColor(PColor: Pointer);


begin
ColorForm := TColorForm.CreateWithColor(PColor, Self);
ColorForm.ShowModal;
ColorForm.Free;
end;
UpdateButtonClick crée une chaîne nommée MainColor. L’adresse de MainColor
est transmise à la fonction GetColor qui crée ColorForm en transmettant comme
argument au constructeur un pointeur sur MainColor . Dès que ColorForm est
fermée, elle est détruite mais le nom de la couleur sélectionnée, s’il y en a une,
est préservé dans MainColor. Sinon, MainColor contient une chaîne vide, ce qui
indique clairement que l’utilisateur est sorti de ColorForm sans sélectionner une
couleur.
Cet exemple utilise une variable chaîne pour stocker des informations provenant
de la fiche modale. Il est possible d’utiliser des objets plus complexes en fonction
de vos besoins. N’oubliez jamais qu’il faut laisser à la fiche appelante un moyen
de savoir que la fiche modale a été fermée sans modification, ni sélection (dans
l’exemple précédent en attribuant par défaut à MainColor une chaîne vide).

Réutilisation des composants et des groupes de


composants
Delphi offre différentes possibilités d’enregistrer et de réutiliser le travail réalisé
avec les composants de la VCL :
• Les modèles de composants permettent de configurer et d’enregistrer facilement
et rapidement des groupes de composants. Voir “Création et utilisation des
modèles de composants” à la page 5-13.
• Vous pouvez enregistrer les fiches, les modules de données et les projets dans
le Référentiel. Vous disposez ainsi d’une base de données centrale composée
d’éléments réutilisables et vous pouvez utiliser l’héritage de fiche pour diffuser
les modifications. Voir “Utilisation du référentiel d’objets” à la page 2-40.
• Vous pouvez enregistrer des cadres sur la palette de composants ou dans le
référentiel. Les cadres utilisent l’héritage de fiche et peuvent être incorporés
dans des fiches ou dans d’autres cadres. Voir “Manipulation des cadres” à la
page 5-14.
• La création d’un composant personnalisé est la manière la plus complexe de
réutiliser du code, mais procure le plus de souplesse. Voir le chapitre 31,
“Présentation générale de la création d’un composant.”

5-12 Guide du développeur


Création et utilisation des modèles de composants

Création et utilisation des modèles de composants


Vous pouvez créer des modèles composés d'un ou de plusieurs composants.
Après avoir organisé les composants sur une fiche, défini leurs propriétés et écrit
du code pour eux, enregistrez-les sous la forme d'un modèle de composant. Par la
suite, lorsque vous sélectionnez le modèle à partir de la palette de composants,
vous pouvez placer les composants préconfigurés sur une fiche en une seule
étape ; toutes les propriétés et tout le code de gestion d'événement associés sont
simultanément ajoutés à votre projet.
Une fois que vous avez placé un modèle sur une fiche, vous pouvez
repositionner les composants indépendamment les uns des autres, redéfinir leurs
propriétés et créer ou modifier des gestionnaires d'événements qui leur sont
associés, comme si vous placiez chaque composant un par un.
Pour créer un modèle de composant :
1 Placez et organisez les composants sur une fiche. Dans l'inspecteur d'objet,
définissez leurs propriétés et événements comme souhaité.
2 Sélectionnez les composants. La manière la plus simple de sélectionner
plusieurs composants consiste à faire glisser le pointeur de la souris au-dessus
d'eux. Des poignées grises apparaissent dans les coins de chaque composant
sélectionné.
3 Choisissez Composant|Créer un modèle de composant.
4 Spécifiez un nom pour le modèle de composant dans la boîte texte Nom de
composant. Le nom proposé par défaut est le type du premier composant
sélectionné à l'étape 2 suivi du mot "Template". Par exemple, si vous
sélectionnez un libellé puis une boîte texte, le nom proposé est
"TLabelTemplate". Vous pouvez modifier ce nom, en veillant à ne pas utiliser
un nom de composant existant.
5 Dans la boîte texte Page de palette, spécifiez la page de la palette de
composants dans laquelle vous souhaitez placer le modèle. Si vous spécifiez
une page qui n'existe pas, une nouvelle page est créée lorsque vous
enregistrez le modèle.
6 Sous Icône de palette, sélectionnez une image bitmap pour représenter le
modèle sur la palette. L'image bitmap proposée par défaut est celle utilisée
par le type du premier composant sélectionné à l'étape 2. Pour rechercher
d'autres images bitmap, cliquez sur Changer. Les dimensions de l'image
bitmap que vous choisissez ne doivent pas dépasser 24 pixels sur 24 pixels.
7 Cliquez sur OK.
Pour supprimer des modèles de la palette de composants, choisissez
Composant|Configurer la palette.

Conception de l’interface utilisateur des applications 5-13


Manipulation des cadres

Manipulation des cadres


Un cadre (TFrame), comme une fiche, est un conteneur pour d'autres composants.
Il utilise le même mécanisme de possession que les fiches lorsque les composants
y sont instanciés et détruits automatiquement, et la même relation parent-enfant
pour la synchronisation des propriétés de composants. A divers égards, un cadre
s'apparente davantage à un composant personnalisé qu'à une fiche. Les cadres
peuvent être enregistrés sur la palette de composants pour en faciliter la
réutilisation et imbriqués dans des fiches, d'autres cadres ou d'autres objets
conteneur. Une fois qu'un cadre a été créé et enregistré, il continue de
fonctionner comme une unité et d'hériter des modifications des composants qu'il
contient (y compris d’autres cadres). Lorsqu'un cadre est incorporé dans un autre
cadre ou dans une fiche, il continue d'hériter des modifications apportées au
cadre dont il dérive.

Création des cadres


Pour créer un cadre vide, choisissez Fichier|Nouveau cadre, ou Fichier|Nouveau
et double-cliquez sur Cadre. Vous pouvez alors déposer des composants (y
compris d'autres cadres) sur le nouveau cadre.
Il est généralement préférable,-bien que non nécessaire,-d'enregistrer les cadres
en tant que partie d’un projet. Si vous souhaitez créer un projet ne contenant
que des cadres et aucune fiche, choisissez Fichier|Nouvelle application, fermez la
nouvelle fiche et la nouvelle unité sans les enregistrer, puis choisissez Fichier|
Nouveau cadre et enregistrez le projet.
Remarque Lorsque vous enregistrez des cadres, évitez d'utiliser les noms par défaut Unit1,
Project1 etc., car ils peuvent être à la source de conflits au moment de l'utilisation
ultérieure des cadres.
A la conception, vous pouvez afficher n'importe quel cadre contenu dans le
projet en cours en choisissant Voir|Fiches et en sélectionnant le cadre. Comme
dans le cas des fiches et des modules de données, vous pouvez passer du
concepteur de fiche au fichier .DFM du cadre en cliquant avec le bouton droit et
en choisissant Voir comme fiche ou Voir comme texte.

Ajout de cadres à la palette de composants


Les cadres sont ajoutés à la palette de composants comme les modèles de
composants. Pour ajouter un cadre à la palette de composants, ouvrez le cadre
dans le concepteur de fiche (vous ne pouvez pas utiliser un cadre incorporé dans
un autre composant), cliquez avec le bouton droit sur le cadre et choisissez
Ajouter à la palette. Lorsque la boîte de dialogue Information modèle de
composant s'ouvre, sélectionnez un nom, une page de palette et une icône pour
le nouveau modèle.

5-14 Guide du développeur


Manipulation des cadres

Utilisation et modification des cadres


Pour utiliser un cadre dans une application, vous devez le placer, directement ou
indirectement, sur une fiche. Vous pouvez ajouter des cadres directement sur des
fiches, sur d'autres cadres ou sur d'autres objets conteneur, comme des volets et
des boîtes de défilement.
Le concepteur de fiche permet d'ajouter un cadre à une application de deux
manières :
• Sélectionnez un cadre à partir de la palette de composants et déposez-le sur
une fiche, un autre cadre ou un autre objet conteneur. Si nécessaire, le
concepteur de fiche demande s'il est possible d'inclure le fichier unité du
cadre dans votre projet.
• Sélectionnez Cadres à partir de la page Standard de la palette de composants
et cliquez sur une fiche ou un autre cadre. Une boîte de dialogue s'ouvre sur
une liste de cadres figurant déjà dans votre projet ; sélectionnez-en un et
cliquez sur OK.
Lorsque vous déposez un cadre sur une fiche ou un autre conteneur, Delphi
déclare une nouvelle classe qui descend du cadre que vous avez séléctionné. De
même, lorsque vous ajoutez une nouvelle fiche à un projet, Delphi déclare une
nouvelle classe qui descend de TForm. Cela signifie que les modifications
apportées ultérieurement au cadre d'origine (ancêtre) sont répercutées sur le
cadre incorporé, mais que les modifications apportées au cadre incorporé ne sont
pas répercutées sur le cadre ancêtre.
Supposons que vous souhaitiez regrouper des composants d’accès aux données
et des contrôles orientés données en vue d'une utilisation fréquente,
éventuellement dans plusieurs applications. Pour ce faire, vous pourriez
rassembler les composants dans un modèle de composant ; mais si vous
commencez à utiliser le modèle et changez d'avis ultérieurement sur
l'organisation des contrôles, vous devez faire marche arrière et modifier
manuellement dans chaque projet la partie sur laquelle le modèle a été placé. Par
contre, si vous placez vos composants base de données dans un cadre, les
modifications ultérieures ne doivent être apportées que dans un seul endroit ; les
modifications apportées à un cadre d'origine sont automatiquement répercutées
sur ses descendants incorporés lors de la recompilation des projets.
Parallèlement, vous pouvez modifier n'importe quel cadre incorporé sans affecter
le cadre d'origine ni aucun de ses descendants incorporés.
Figure 5.1 Cadre avec des contrôles orientés données et un composant source de
données

Conception de l’interface utilisateur des applications 5-15


Création et gestion de menus

Outre une simplification de gestion, les cadres procurent une efficacité


supplémentaire dans l'utilisation des ressources. Par exemple, pour utiliser une
image bitmap ou un autre graphique dans une application, vous pouvez charger
le graphique dans la propriété Picture d’un contrôle TImage. Toutefois, si vous
utilisez fréquemment le même graphique dans une application, chaque objet
Image que vous placez sur une fiche génère une autre copie du graphique ajouté
au fichier ressource de la fiche. Cela est également vrai si vous définissez
TImage.Picture une fois et enregistrez le contrôle Image en tant que modèle de
composant. Une meilleure solution consiste à déposer l'objet Image sur un cadre,
à y charger le graphique puis à utiliser le cadre là où vous souhaitez que le
graphique apparaisse. Cela génère des fichiers fiche moins volumineux et
présente en outre la possibilité de modifier le graphique partout où il figure en
modifiant l'objet Image sur le cadre d’origine.

Partage des cadres


Vous pouvez partager un cadre avec les autres développeurs de deux manières :
• Ajoutez le cadre au référentiel d’objet.
• Distribuez l’unité du cadre (.PAS) et les fichiers fiche (.DFM).
Pour ajouter un cadre au référentiel, ouvrez n’importe quel projet contenant le
cadre, cliquez avec le bouton droit dans le concepteur de fiche et choisissez
Ajouter au référentiel. Pour plus d’informations, voir “Utilisation du référentiel
d’objets” à la page 2-40.
Si vous envoyez une unité de cadre et les fichiers fiche à d'autres développeurs,
ils peuvent les ouvrir et les ajouter à la palette de composants. Si le cadre
contient d'autres cadres, ils devront l'ouvrir en tant que partie d'un projet.

Création et gestion de menus


Les menus constituent pour les utilisateurs un moyen commode d’exécuter des
commandes regroupées logiquement. Le concepteur de menus vous permet
d’ajouter facilement à une fiche un menu prédéfini ou personnalisé. Ajoutez
simplement un composant menu à la fiche, ouvrez le concepteur de menus et
saisissez directement les éléments de menu dans la fenêtre du concepteur de
menus. Vous pouvez ajouter ou supprimer des éléments de menu et utiliser le
glisser-déplacer pour réorganiser les éléments à la conception.
Vous n’avez même pas besoin d’exécuter votre programme pour voir le résultat :
ce que vous concevez est immédiatement visible dans la fiche et apparaît
exactement comme à l’exécution. En utilisant du code, vous pouvez également
modifier les menus à l’exécution afin de proposer à l’utilisateur davantage
d’informations ou d’options.

5-16 Guide du développeur


Création et gestion de menus

Ce chapitre décrit la manière d’utiliser le concepteur de menusDelphi pour


concevoir des barres de menus et des menus surgissants (locaux). Les sujets
suivants, concernant la manipulation des menus à la conception et à l’exécution,
sont abordés :
• Ouverture du concepteur de menus
• Conception de menus
• Edition des éléments de menu dans l’inspecteur d’objets
• Utilisation du menu contextuel du concepteur de menus
• Utilisation des modèles de menu
• Enregistrement d’un menu comme modèle
• Ajout d’images à des éléments de menu
Figure 5.2 Terminologie des menus Delphi
Eléments de menu de la barre de menu
Touche de raccourci
Eléments de menu d’une liste de menus

Ligne de séparation Raccourci clavier

Pour des informations sur la façon de lier un élément de menu à du code qui
s’exécute lorsque l’élément est sélectionné, voir “Association d’événements de menu
à des gestionnaires d’événements” à la page 2-30.

Ouverture du concepteur de menus


Pour utiliser le concepteur de menus, commencez par ajouter à votre fiche un
composant menu principal (MainMenu) ou menu surgissant (PopupMenu). Ces
deux composants menu se trouvent dans la page Standard de la palette des
composants.
Figure 5.3 Composants menu principal et menu surgissant
Composant menu principal (MainMenu)
Composant menu surgissant (PopupMenu)

Un composant menu principal (MainMenu) crée un menu attaché à la barre de


titre de la fiche. Un composant menu surgissant (PopupMenu) crée un menu qui
apparaît quand l’utilisateur clique avec le bouton droit de la souris dans la fiche.
Les menus surgissants n’ont pas de barre de menu.
Pour ouvrir le concepteur de menus, sélectionnez un composant menu de la
fiche, puis utilisez l’une des méthodes suivantes :
• Double-cliquez sur le composant menu.
• Dans la page Propriétés de l’inspecteur d’objets, sélectionnez la propriété Items
puis double-cliquez sur [Menu] dans la colonne des valeurs ou cliquez sur le
bouton points de suspension (...).

Conception de l’interface utilisateur des applications 5-17


Création et gestion de menus

Le concepteur de menus apparaît, le premier élément de menu (vide) est


sélectionné dans le concepteur et la propriété Caption est mise en évidence
dans l’inspecteur d’objets.
Figure 5.4 Concepteur de menus d’un menu principal
Barre de titre (affiche la propriété Name
pour les composants menu principal
Barre de menu
Emplacement pour les éléments de menu

Le concepteur de menus affiche


les éléments de menu en mode
WYSIWYG quand vous concevez
le menu.

Un objet TMenuItem est créé et la


propriété Name est affectée avec
l’intitulé (Caption) spécifié pour
l’élément de menu (moins les
caractères interdits et plus un
suffixe numérique).

Figure 5.5 Concepteur de menus d’un menu surgissant

Emplacement pour le premier élément


de menu

5-18 Guide du développeur


Création et gestion de menus

Conception de menus
Vous devez ajouter un composant menu à vos fiches pour chaque menu devant
apparaître dans l’application. Vous pouvez créer chaque structure de menu à
partir de zéro ou partir de l’un des modèles de menu prédéfinis.
Cette section décrit les principes de base de la création de menus à la
conception. Pour davantage d’informations sur les modèles de menu, voir
“Utilisation des modèles de menu” à la page 5-27.

Nom des menus


Comme pour tous les composants, Delphi donne un nom par défaut au
composant menu que vous ajoutez à une fiche, par exemple MainMenu1. Vous
pouvez attribuer au menu un nom plus explicite respectant les conventions de
nom du Pascal Objet.
Delphi ajoute le nom du menu à la déclaration de type de la fiche et le nom du
menu apparaît dans la liste des composants.

Nom des éléments de menu


A la différence des composants menu, vous devez donner vous-même
explicitement un nom aux éléments de menu quand vous les ajoutez à la fiche.
Vous pouvez le faire de deux manières :
• En saisissant directement la valeur de la propriété Name.
• En saisissant d’abord la valeur de la propriété Caption, puis en laissant Delphi
en dériver une valeur pour la propriété Name.
Si, par exemple, vous spécifiez Fichier comme valeur de la propriété Caption,
DelphiDelphi affecte la valeur Fichier1 à la propriété Name de l’élément de
menu. Si vous renseignez d’abord la propriété Name avant de renseigner la
propriété Caption, Delphi laisse la propriété Caption vide jusqu’à ce que vous
saisissiez une valeur.
Remarque Si vous saisissez dans la propriété Caption des caractères qui ne sont pas
autorisés dans un identificateur Object Pascal, Delphi modifie la propriété
Name en conséquence. Si par exemple, l’intitulé commence par un chiffre,
Delphi fait précéder le chiffre d’un caractère pour en dériver la valeur de la
propriété Name.
Le tableau suivant donne des exemples de ce processus en supposant que tous
ces éléments sont placés dans la même barre de menu.

Tableau 5.1 Exemples d’intitulés et des noms dérivés


Intitulé du
composant Nom dérivé Explication
&Fichier Fichier1 Retire le &
&Fichier (une Fichier2 Numérote les éléments répétés
deuxième fois)

Conception de l’interface utilisateur des applications 5-19


Création et gestion de menus

Tableau 5.1 Exemples d’intitulés et des noms dérivés


Intitulé du
composant Nom dérivé Explication
1234 N12341 Ajoute une lettre au début et numérote en fin
1234 (une deuxième N12342 Ajoute un nombre pour que le nom dérivé ne
fois) soit plus ambigu.
$@@@# N1 Supprime tous les caractères non standard,
ajoute une lettre au début et un numéro
d’ordre
- (signe moins) N2 Numérote cette deuxième occurrence d’un
intitulé sans aucun caractère standard

Comme pour le composant menu, Delphi ajoute le nom des éléments de menu à
la déclaration de type de la fiche et leur nom apparaît dans la liste des
composants.

Ajout, insertion et suppression d’éléments de menu


Les procédures suivantes décrivent la manière d’effectuer les opérations de base
intervenant dans la conception d’une structure de menu. Chaque procédure
suppose que la fenêtre du concepteur de menus est déjà ouverte.
Pour ajouter des éléments de menu à la conception :
1 Sélectionnez la position à laquelle vous voulez créer l’élément de menu.
Si vous venez juste d’ouvrir le concepteur de menus, la première position est
déjà sélectionnée dans la barre de menu.
2 Commencez à saisir l’intitulé. Vous pouvez aussi commencer à saisir la
propriété Name en plaçant le curseur dans l’inspecteur d’objets et en saisissant
une valeur. Dans ce cas, vous devez resélectionner la propriété Caption et
entrer une valeur.
3 Appuyez sur Entrée.
L’emplacement suivant d’élément de menu est sélectionné.
Si vous entrez d’abord la propriété Caption, utilisez les touches de
déplacement pour revenir dans l’élément de menu que vous venez de saisir.
Vous pouvez constater que Delphi a renseigné la propriété Name en partant
de la valeur saisie dans l’intitulé (voir “Nom des éléments de menu” à la
page 5-19).
4 Continuez à saisir la valeur des propriétés Name et Caption pour chaque
élément de menu à ajouter ou appuyez sur Echap pour revenir à la barre de
menu.
Utilisez les touches de déplacement pour passer de la barre de menu au
menu, puis pour vous déplacer dans les éléments de la liste ; appuyez sur
Entrée pour achever une action. Pour revenir à la barre de menu, appuyez sur
Echap.

5-20 Guide du développeur


Création et gestion de menus

Pour insérer un nouvel élément de menu vide :


1 Placez le curseur sur un élément de menu.
2 Appuyez sur Inser.
Dans la barre de menu, les éléments de menu sont insérés à gauche de
l’élément sélectionné et au-dessus de l’élément sélectionné dans la liste de
menus.
Pour supprimer un élément de menu :
1 Placez le curseur sur l’élément de menu à supprimer.
2 Appuyez sur Suppr.
Remarque Vous ne pouvez pas supprimer l’emplacement par défaut qui apparaît en
dessous du dernier élément ajouté à la liste de menus ou à côté du dernier
élément de la barre de menu. Cet emplacement n’apparaît pas dans le menu à
l’exécution.

Ajout de lignes de séparation


Les lignes de séparation insèrent une ligne entre deux éléments de menu. Vous
pouvez utiliser les lignes de séparation pour matérialiser des groupes de
commandes dans une liste de menus ou simplement pour réaliser une séparation
visuelle dans une liste.
Pour faire d’un élément de menu une ligne de séparation, entrez un tiret (-)
comme intitulé.

Spécification de touches accélératrices et de raccourcis clavier


Les touches accélératrices permettent à l’utilisateur d’accéder à une commande
de menu à partir du clavier en appuyant sur Alt+ la lettre appropriée indiquée
dans le code en la faisant précéder du symbole &. La lettre suivant le symbole &
apparaît soulignée dans le menu.
Delphi vérifie automatiquement si des touches accélératrices sont dupliquées et les
ajuste à l’exécution. Ainsi, les menus conçus dynamiquement à l’exécution ne
contiennent aucune touche accélératrice dupliquée et tous les éléments de menu
disposent d’une touche accélératrice. Vous pouvez désactiver cette vérification
automatique en attribuant à la propriété AutoHotkeys d’un élément de menu la valeur
maManual.
Pour spécifier une touche accélératrice
• Ajoutez un & devant la lettre appropriée de l’intitulé.
Ainsi, pour ajouter une commande de menu Enregistrer dont la lettre E sert
de touche accélératrice, entrez &Enregistrer.
Les raccourcis clavier permettent à l’utilisateur d’effectuer l’action directement
sans accéder au menu, simplement en appuyant sur la combinaison de touches
du raccourci clavier.

Conception de l’interface utilisateur des applications 5-21


Création et gestion de menus

Pour spécifier un raccourci clavier :


• Utilisez l’inspecteur d’objets pour saisir une valeur dans la propriété ShortCut
ou sélectionnez une combinaison de touches dans la liste déroulante.
Cette liste ne propose qu’un sous-ensemble de toutes les combinaisons
utilisables.
Quand vous ajoutez un raccourci, il apparaît à l’exécution à côté de l’intitulé de
l’élément de menu.
Attention A la différence des touches accélératrices, la présence de raccourcis clavier dupliqués
n’est pas automatiquement vérifiée. Vous devez vous-même en assurer l’unicité.

Création de sous-menus
La plupart des menus d’application contiennent des listes déroulantes qui
apparaissent à côté d’un élément de menu afin de proposer des commandes
associées supplémentaires. De telles listes sont signalées par un pointeur à droite
de l’élément de menu. Delphi gère les niveaux de sous-menus sans aucune
limitation dans l’imbrication.
Une telle organisation de votre structure de menu permet d’économiser de la
place verticalement. Néanmoins, pour optimiser la conception, il est préférable de
ne pas utiliser plus de deux ou trois niveaux de menus dans la conception de
votre interface. Dans le cas des menus surgissants, il est préférable de n’utiliser
au maximum qu’un niveau de sous-menu.
Figure 5.6 Structure de menus imbriqués

Elément de
menu de la barre

Elément de menu
d’une liste de

Elément de menu
imbriqué

Pour créer un sous-menu :


1 Sélectionnez l’élément de menu sous lequel vous voulez créer un sous-menu.
2 Appuyez sur Ctrl→ afin de créer le premier emplacement ou cliquez sur le
bouton droit de la souris et choisissez Créer un sous-menu.
3 Entrez l’intitulé de l’élément de sous-menu ou déplacez un élément de menu
existant sur cet emplacement.
4 Appuyez sur Entrez ou ↓ pour créer l’emplacement suivant.
5 Répétez les étapes 3 et 4 pour chaque élément du sous-menu.
6 Appuyez sur Echap pour revenir au niveau de menu précédent.

5-22 Guide du développeur


Création et gestion de menus

Création de sous-menus par déplacement de menus existants


Vous pouvez également créer un sous-menu en insérant un élément de la barre de
menu (ou d’un modèle de menu) entre les éléments de menu d’une liste. Quand
vous déplacez un menu dans une structure de menu existante, tous ses éléments
de menu associés se déplacent, ce qui crée directement un sous-menu. Cela
s’applique également aux sous-menus : le déplacement d’un élément de menu dans
un sous-menu existant peut ainsi créer un niveau supplémentaire d’imbrication.

Déplacement d’éléments de menu


A la conception, vous pouvez déplacer les éléments de menu en utilisant le
glisser-déplacer. Vous pouvez déplacer des éléments de menu dans la barre de
menu, à un emplacement différent dans la liste de menus ou dans un autre
composant menu.
La seule limitation à ces déplacements est de nature hiérarchique : vous ne
pouvez pas déplacer un élément de menu de la barre de menu à l’intérieur de
son propre menu ; de même, vous ne pouvez pas déplacer un élément de menu
à l’intérieur de son propre sous-menu. Par contre, vous pouvez toujours déplacer
un élément dans un menu différent quelle que soit sa position d’origine.
Quand vous faites glisser un élément, la forme du curseur change afin
d’indiquer si vous pouvez déposer l’élément de menu à l’emplacement du
pointeur de la souris. Quand vous déplacez un élément de menu, tous les
éléments de menu situés en dessous sont également déplacés.
Pour déplacer un élément de menu à l’intérieur de la barre de menu :
1 Faites glisser l’élément de menu jusqu’à ce que la pointe du curseur de
déplacement désigne la nouvelle position.
2 Relâchez le bouton de la souris pour déposer l’élément de menu à la nouvelle
position.
Pour déplacer un élément de menu dans une liste de menus :
1 Déplacez l’élément de menu le long de la barre de menu jusqu’à ce que le
pointeur du curseur de déplacement désigne le nouveau menu.
Cela force l’ouverture du menu, ce qui vous permet d’amener l’élément à son
nouvel emplacement.
2 Faites glisser l’élément de menu dans la liste et relâchez le bouton de la souris
pour déposer l’élément de menu à sa nouvelle position.

Ajout d’images à des éléments de menu


Des images peuvent aider les utilisateurs à naviguer dans les menus en associant
des glyphes et des images à des actions d’élément de menu, comme les images
des barres d’outils. Pour ajouter une image à un élément de menu :
1 Déposez un composant TMainMenu ou TPopupMenu dans une fiche.
2 Déposez un objet TImageList dans la fiche.
3 Ouvrez l’éditeur de liste d’images en double-cliquant sur l’objet TImageList.

Conception de l’interface utilisateur des applications 5-23


Création et gestion de menus

4 Choisissez Ajouter pour sélectionner le bitmap ou le groupe de bitmaps que


vous voulez utiliser dans le menu. Choisissez OK.
5 Affectez l’objet liste d’images que vous venez de créer à la propriété Images
du composant TMainMenu ou TPopupMenu.
6 Créez vos éléments de menu et vos sous-menus comme décrit plus haut.
7 Sélectionnez dans l’inspecteur d’objets l’élément de menu pour lequel vous
voulez spécifier une image et affectez à sa propriété ImageIndex le numéro
correspondant dans la liste d’images (la valeur par défaut de la propriété
ImageIndex est -1 : pas d’image affichée).
Remarque Pour un affichage correct dans les menus, utilisez des images de 16 sur 16 pixels.
Même si vous pouvez utiliser d’autres tailles pour les images placées dans les
menus, il peut y avoir des problèmes d’alignement si vous utilisez des images
plus grandes ou plus petites que 16 x 16 pixels.

Affichage du menu
Vous pouvez voir votre menu dans la fiche à la conception sans exécuter le code
de votre programme. Les composants menu surgissant sont visibles dans la fiche
à la conception, mais pas le menu surgissant lui-même. Utilisez le concepteur de
menus pour visualiser un menu surgissant à la conception.
Pour visualiser le menu :
1 Si la fiche n’est pas visible, cliquez dans la fiche ou dans le menu Voir,
choisissez la fiche que vous voulez voir.
2 Si la fiche contient plusieurs menus, sélectionnez le menu à visualiser dans la
liste déroulante de la propriété Menu de la fiche.
Le menu apparaît dans la fiche exactement tel qu’il apparaîtra à l’exécution du
programme.

Edition des éléments de menu dans l’inspecteur


d’objets
Cette section a décrit jusqu’à présent comment initialiser diverses propriétés des
éléments de menu (par exemple, les propriétés Name ou Caption) en utilisant le
concepteur de menus.
Cette section a également décrit comment initialiser certaines propriétés (par
exemple, ShortCut) des éléments de menu directement dans l’inspecteur d’objets
comme vous le faites pour les autres composants sélectionnés dans la fiche.
Quand vous modifiez un élément de menu en utilisant le concepteur de menus,
ses propriétés restent affichées dans l’inspecteur d’objets. Vous pouvez donc faire
passer la focalisation dans l’inspecteur d’objets et y poursuivre la modification de
l’élément de menu. Vous pouvez également sélectionner l’élément de menu dans
la liste des composants de l’inspecteur d’objets et modifier ses propriétés sans
même ouvrir le concepteur de menus.

5-24 Guide du développeur


Création et gestion de menus

Pour fermer la fenêtre du concepteur de menus tout en poursuivant la


modification des éléments de menu :
1 Faites passer la focalisation de la fenêtre du concepteur de menus à
l’inspecteur d’objets en cliquant dans la page Propriétés de l’inspecteur
d’objets.
2 Fermez normalement le concepteur de menus.
La focalisation reste dans l’inspecteur d’objets où vous pouvez continuer à
modifier les propriétés de l’élément de menu sélectionné. Pour éditer un autre
élément de menu, sélectionnez-le dans la liste des composants.
Pour des informations sur l’affectation des gestionnaires d’événements aux
menus, voir “Association d’événements de menu à des gestionnaires
d’événements” à la page 2-30.

Utilisation du menu contextuel du concepteur de


menus
Le menu contextuel du concepteur de menus propose un accès rapide aux
commandes les plus couramment utilisées du concepteur de menus et aux
options des modèles de menu. Pour davantage d’informations sur les modèles de
menu, voir “Utilisation des modèles de menu” à la page 5-27.
Pour afficher le menu contextuel, cliquez avec le bouton droit de la souris dans
la fenêtre du concepteur de menus ou appuyez sur Alt+F10 quand le curseur est
dans la fenêtre du concepteur de menus.

Commandes du menu contextuel


Le tableau suivant résume les commandes du menu contextuel du concepteur de
menus.

Tableau 5.2 Commandes du menu contextuel du concepteur de menus


Commande de menu Action
Insérer Insère un emplacement au-dessus ou à gauche du curseur.
Supprimer Supprime l’élément de menu sélectionné (et tous ses éventuels
sous-éléments).
Créer un sous-menu Crée un emplacement à un niveau imbriqué et ajoute un
pointeur à droite de l’élément de menu sélectionné.
Sélectionner un menu Ouvre une liste des menus dans la fiche en cours. Double-
cliquez sur un nom de menu pour l’ouvrir dans le concepteur
de menus.
Enregistrer comme modèle Ouvre la boîte de dialogue Enregistrement de modèle qui
vous permet d’enregistrer un menu pour une réutilisation
ultérieure.
Insérer depuis le modèle Ouvre la boîte de dialogue Insertion de modèle qui vous
permet de sélectionner le modèle à réutiliser.

Conception de l’interface utilisateur des applications 5-25


Création et gestion de menus

Tableau 5.2 Commandes du menu contextuel du concepteur de menus (suite)


Commande de menu Action
Supprimer les modèles Ouvre la boîte de dialogue Suppression de modèles qui vous
permet de supprimer des modèles existants.
Insérer depuis la ressource Ouvre la boîte de dialogue Insertion de menu depuis une
ressource qui vous permet de choisir le fichier .MNU ouvert
dans la fiche en cours.

Changement de menu à la conception


Si vous concevez plusieurs menus pour votre fiche, vous pouvez utiliser le
concepteur de menus ou l’inspecteur d’objets pour sélectionner un menu ou
passer d’un menu à l’autre.
Pour utiliser le menu contextuel afin de passer d’un menu de la fiche à un
autre :
1 Cliquez avec le bouton droit de la souris dans le concepteur de menus et
choisissez Sélectionnez un menu.
La boîte de dialogue Sélection de menu s’affiche.
Figure 5.7 Boîte de dialogue Sélection de menu

Cette boîte de dialogue énumère tous les menus associés à la fiche dont les
menus sont ouverts dans le concepteur de menus.
2 Dans la liste de la boîte de dialogue Sélection de menu, choisissez le menu
que vous voulez voir ou modifier.
Pour utiliser l’inspecteur d’objets afin de passer d’un menu de la fiche à un
autre :
1 Sélectionnez la fiche dont vous voulez choisir un menu.
2 Dans la liste des composants, sélectionnez le menu que vous voulez modifier.
3 Dans la page Propriétés de l’inspecteur d’objets, sélectionnez la propriété Items
de ce menu, puis cliquez sur le bouton points de suspension ou double-
cliquez sur [Menu].

5-26 Guide du développeur


Création et gestion de menus

Utilisation des modèles de menu


Delphi propose plusieurs menus pré-conçus (des modèles de menu) qui
contiennent des commandes d’utilisation courante. Vous pouvez utiliser ces
menus dans vos applications sans les modifier (sauf pour ajouter le code) ou
vous pouvez les utiliser comme point de départ en les personnalisant comme
vous le feriez avec un menu que vous avez créé. Les modèles de menu ne
contiennent pas de code de gestionnaire d’événement.
Les modèles de menu livrés avec Delphi sont stockés dans le sous-répertoire BIN
de l’installation par défaut. Ces fichiers ont l’extension .DMT (Delphi Menu
Template).
Vous pouvez enregistrer comme modèle tout menu que vous avez conçu dans le
concepteur de menus. Après avoir enregistré un menu comme modèle, vous
pouvez l’utiliser comme les autres modèles de menu. Si vous n’avez plus besoin
d’un modèle de menu, vous pouvez le retirer de la liste.
Pour ajouter un modèle de menu à votre application :
1 Cliquez avec le bouton droit de la souris dans le concepteur de menus et
choisissez Insérer depuis le modèle.
(s’il n’y a pas de modèle, l’option Insérer depuis le modèle apparaît estompée
dans le menu contextuel.)
La boîte de dialogue Insertion de modèle est ouverte et affiche une liste des
modèles de menu disponibles.
Figure 5.8 Boîte de dialogue Insertion de modèle des menus

2 Sélectionnez le modèle de menu que vous voulez insérer, puis appuyez sur
Entrée ou choisissez OK.
Cela insère le menu dans votre fiche à la position courante du curseur. Si, par
exemple, le curseur est positionné sur un élément de menu d’une liste, le
modèle de menu est inséré en dessous de l’élément sélectionné. Si le curseur
est dans la barre de menu, le modèle de menu est inséré à gauche du curseur.

Conception de l’interface utilisateur des applications 5-27


Création et gestion de menus

Pour supprimer un modèle de menu :


1 Cliquez avec le bouton droit de la souris dans le concepteur de menus et
choisissez Supprimer les modèles.
(S’il n’y a pas de modèles, l’option Supprimer les modèles apparaît estompée
dans le menu contextuel.)
La boîte de dialogue Suppression de modèles est ouverte et affiche une liste
des modèles existants.
2 Sélectionnez le modèle de menu à supprimer et appuyez sur Suppr.
Delphi supprime le modèle dans la liste des modèles et sur le disque dur.

Enregistrement d’un menu comme modèle


Il est possible d’enregistrer comme modèle tout menu que vous avez conçu afin
de pouvoir le réutiliser. Vous pouvez employer les modèles de menu pour
donner un aspect homogène à vos applications ou les utiliser comme points de
départ que vous personnalisez.
Les modèles de menu que vous enregistrez sont stockés dans le sous-répertoire
BIN dans des fichiers .DMT.
Pour enregistrer un menu comme modèle
1 Concevez le menu que vous voulez pouvoir réutiliser.
Ce menu peut contenir de nombreux éléments, commandes et sous-menus :
tout dans le contenu de la fenêtre active du concepteur de menus est
enregistré comme un seul menu réutilisable.
2 Cliquez avec le bouton droit de la souris dans le concepteur de menus et
choisissez Enregistrer comme modèle.
La boîte de dialogue Enregistrement de modèle s’ouvre
Figure 5.9 .Boîte de dialogue Enregistrement de modèle des menus

5-28 Guide du développeur


Création et gestion de menus

3 Dans la boîte de saisie Description du modèle, entrez une brève description de


ce menu, puis choisissez OK.
La boîte de dialogue Enregistrement du modèle se ferme en enregistrant votre
menu et vous revenez dans le concepteur de menus.
Remarque La description que vous saisissez n’est affichée que dans les boîtes de dialogue
Enregistrement du modèle, Insertion de modèle et Suppression de modèles. Elle
n’a aucun rapport avec les propriétés Name ou Caption du menu.

Conventions de nom pour les éléments et les gestionnaires


d’événements des modèles de menu
Quand vous enregistrez un menu comme modèle, Delphi n’enregistre pas sa
propriété Name car chaque menu doit disposer d’un nom unique dans la portée
de son propriétaire (la fiche). Cependant, quand vous insérez le menu comme
modèle dans une nouvelle fiche en utilisant le concepteur de menus, Delphi
génère alors de nouveaux noms pour lui et tous ses éléments.
Si, par exemple, vous avez comme modèle un menu Fichier, dans le menu
d’origine, que vous avez nommé MonFichier. Si vous l’insérez comme modèle
dans un nouveau menu, Delphi le nomme Fichier1. Si vous l’insérez dans un
menu ayant déjà un élément de menu nommé Fichier1, Delphi le nomme Fichier2.
Delphi n’enregistre aucun gestionnaire d’événement OnClick associé au menu
enregistré comme modèle car il n’y a pas moyen de savoir si le code est
applicable dans la nouvelle fiche. Quand vous générez un nouveau gestionnaire
d’événement pour un élément du modèle de menu, Delphi génère également le
nom du gestionnaire d’événement.
Il est simple d’associer des éléments de menu à des gestionnaires d’événements
existants de la fiche. Pour plus d’informations, voir “Association d’un événement
à un gestionnaire d’événement existant” à la page 2-29.

Manipulation d’éléments de menu à l’exécution


A l’exécution, il est parfois nécessaire d’ajouter à une structure de menu
existante des éléments de menu ; cela permet de proposer davantage
d’informations ou d’options à l’utilisateur. Il est possible d’insérer un élément de
menu en utilisant les méthodes Add ou Insert de l’élément de menu. Vous
pouvez également masquer ou afficher des éléments d’un menu en jouant sur
leur propriété Visible. La propriété Visible détermine si l’élément de menu est
affiché dans le menu. Pour estomper un élément de menu sans le masquer,
utilisez la propriété Enabled.
Pour des exemples utilisant les propriétés Visible et Enabled des éléments de
menu, voir “Désactivation des éléments de menu” à la page 6-11.
Dans des applications MDI ou OLE, vous pouvez également fusionner des
éléments de menu dans une barre de menu existante. Les sections suivantes
décrivent ce processus plus en détail.

Conception de l’interface utilisateur des applications 5-29


Création et gestion de menus

Fusion de menus
Dans les applications MDI, comme l’application exemple éditeur de texte ou
dans les applications client OLE, le menu principal de votre application doit être
capable d’intégrer les éléments de menu d’une autre fiche ou d’un objet serveur
OLE. Ce processus s’appelle la fusion de menus.
Pour préparer des menus à la fusion, vous devez spécifier les valeurs de deux
propriétés :
• Menu, une propriété de la fiche.
• GroupIndex, une propriété des éléments du menu.

Spécification du menu actif : propriété Menu


La propriété Menu spécifie le menu actif de la fiche. Les opérations de fusion de
menu portent uniquement sur le menu actif. Si la fiche contient plusieurs
composants menu, vous pouvez changer le menu actif à l’exécution en modifiant
la valeur de la propriété Menu dans le code. Par exemple :
Form1.Menu := SecondMenu;

Ordre des éléments de menu fusionnés : propriété


GroupIndex
La propriété GroupIndex détermine l’ordre dans lequel les éléments de menu
fusionnés apparaissent dans la barre de menu partagée. La fusion d’éléments de
menu peut remplacer des éléments existants de la barre de menu principale ou
rajouter des éléments.
La valeur par défaut de GroupIndex est 0. Plusieurs règles s’appliquent à la
spécification d’une valeur pour la propriété GroupIndex :
• Les nombres les plus bas apparaissent en premier (plus à gauche) dans le
menu.
Par exemple, affectez la valeur 0 (zéro) à la propriété GroupIndex d’un menu
qui doit apparaître tout à gauche, comme le menu Fichier. De même, spécifiez
une valeur élevée (elle n’a pas besoin d’être consécutive) à un menu qui doit
apparaître tout à droite (comme le menu Aide).
• Pour remplacer des éléments du menu principal, attribuez la même valeur à la
propriété GroupIndex des éléments du menu enfant.
Cela peut s’appliquer à des groupes d’éléments ou à un seul élément. Si, par
exemple, votre fiche principale contient un élément de menu Edition dont la
propriété GroupIndex vaut 1, vous pouvez le remplacer par un ou plusieurs
éléments du menu de la fiche enfant en attribuant également la valeur 1 à leur
propriété GroupIndex.
Si plusieurs éléments du menu enfant ont la même valeur pour GroupIndex,
leur ordre n’est pas modifié quand ils sont fusionnés au menu principal.

5-30 Guide du développeur


Conception de barres d’outils et de barres multiples

• Pour insérer des éléments sans remplacer des éléments du menu principal,
laissez des intervalles numériques entre les éléments du menu principal et
intercalez les numéros de la fiche enfant.
Vous pouvez par exemple, numéroter les éléments du menu principal 0 et 5,
et insérer les éléments du menu enfant en les numérotant 1, 2, 3 et 4.

Importation de fichiers ressource


Delphi peut utiliser des menus conçus avec d’autres applications dans la mesure
où ils utilisent le format standard de fichier ressource Windows (.RC). Vous
pouvez importer ces menus directement dans votre projet Delphi, ce qui vous
évite d’avoir à redéfinir des menus que vous avez conçu par ailleurs.
Pour charger un fichier menu .RC existant :
1 Dans le concepteur de menus, placez le curseur à l’endroit où le menu doit
apparaître.
Le menu importé peut faire partie du menu que vous concevez ou constituer
la totalité d’un menu par lui-même.
2 Cliquez avec le bouton droit de la souris et choisissez Insérer depuis la
ressource.
La boîte de dialogue d’insertion d’un menu depuis une ressource s’ouvre.
3 Dans la boîte de dialogue, sélectionnez le fichier ressource à charger, puis
choisissez OK.
Le menu apparaît dans la fenêtre du concepteur de menus.
Remarque Si votre ficher ressource contient plusieurs menus, vous devez enregistrer chacun
de ses menus dans un fichier ressource séparé avant de pouvoir importer.

Conception de barres d’outils et de barres multiples


Une barre d’outils est un volet, généralement placé en haut d’une fiche (sous la
barre de menu) qui contient des boutons et d’autres contrôles. Une barre multiple
est une sorte de barre d’outils qui affiche des contrôles dans des bandes
déplaçables et redimensionnables. Si plusieurs volets sont alignés sur le haut de
la fiche, ils s’empilent verticalement dans l’ordre de leur ajout.
Vous pouvez placer toutes sortes de contrôles dans une barre d’outils. Outre les
boutons, vous pouvez y placer des grilles de couleur, des barres de défilement,
des libellés, etc.
Il y a plusieurs manières d’ajouter une barre d’outils à une fiche :
• Placez un composant volet (TPanel) dans la fiche et ajoutez-y des contrôles, en
général des turboboutons.

Conception de l’interface utilisateur des applications 5-31


Conception de barres d’outils et de barres multiples

• Utilisez un composant barre d’outils (TToolBar) à la place de TPanel et ajoutez-


lui des contrôles. TToolBar gère les boutons et les autres contrôles en les
disposant en lignes et en ajustant automatiquement leur taille et leur position.
Si vous utilisez des contrôles boutons outils, (TToolButton) dans la barre
d’outils, TToolBar permet simplement de grouper les boutons de manière
fonctionnelle et propose d’autres options d’affichage.
• Utilisez un composant barre multiple (TCoolBar) et ajoutez-lui des contrôles.
La barre multiple affiche des contrôles dans des bandes qui peuvent être
déplacées et redimensionnées de manière indépendante.
La méthode à employer pour implémenter une barre d’outils dépend de votre
application. Le composant volet présente l’avantage de vous donner une maîtrise
totale de l’aspect de la barre d’outils.
Si vous utilisez des composants barre d’outils ou bande multiple, vous êtes
certain que votre application a bien le style d’une application Windows, car vous
utilisez dans ce cas des contrôles natifs de Windows. Si ces contrôles du système
d’exploitation changent à l’avenir, votre application changera également. Par
ailleurs, comme les composants barre d’outils et barre multiple sont fondés sur
des composants standard Windows, votre application nécessite la présence du
fichier COMCTL32.DLL. Les barres d’outils et les barres multiples ne sont pas
autorisés dans les applications WinNT 3.51.
Les sections suivantes expliquent comment :
• Ajouter une barre d’outils et les contrôles turbobouton correspondants en
utilisant le composant volet
• Ajouter une barre d’outils et les contrôles boutons outils correspondants en
utilisant le composant barre d’outils
• Ajouter un composant barre multiple
• Répondre aux clics
• Ajouter des barres d’outils et des barres multiples masquées
• Afficher et masquer des barres d’outils et des barres multiples

Ajout d’une barre d’outils en utilisant un composant


volet
Pour ajouter à une fiche une barre d’outils en utilisant un composant volet :
1 Ajoutez à la fiche un composant volet de la page Standard de la palette des
composants.
2 Affectez la valeur alTop à la propriété Align du volet. Quand il est aligné sur
le haut de la fiche, le volet conserve sa hauteur mais ajuste sa largeur pour
occuper toute la largeur de la zone client de la fiche, et ce même si la fenêtre
change de taille.
3 Ajoutez des turboboutons ou d’autres contrôles dans le volet.

5-32 Guide du développeur


Conception de barres d’outils et de barres multiples

Les turboboutons sont conçus pour fonctionner dans des volets barre d’outils.
Généralement, un turbobouton n’a pas d’intitulé mais seulement une petite
image (appelée un glyphe) qui représente la fonction du bouton.
Les turboboutons ont trois modes de fonctionnement. Ils peuvent :
• Se comporter comme des boutons poussoir normaux.
• Se comporter comme une bascule
• Se comporter comme un ensemble de boutons radio.
Pour implémenter des turboboutons dans des barres d’outils, vous pouvez :
• Ajouter un turbobouton dans un volet barre d’outils
• Affecter un glyphe au turbobouton
• Définir l’état initial du turbobouton
• Créer un groupe de turboboutons
• Utiliser des boutons bascules

Ajout d’un turbobouton à un volet


Pour ajouter un turbobouton à un volet barre d’outils, placez dans le volet un
composant turbobouton de la page Supplément de la palette des composants.
C’est alors le volet et non la fiche qui est le “propriétaire” du turbobouton, donc
déplacer ou masquer le volet déplace ou masque également le turbobouton.
La hauteur par défaut d’un volet est 41 et la hauteur par défaut d’un
turbobouton est 25. Si vous affectez la valeur 8 à la propriété Top de chaque
bouton, ils sont centrés verticalement. Le paramétrage par défaut de la grille
aligne verticalement le turbobouton sur cette position.

Spécification du glyphe d’un turbobouton


Chaque turbobouton a besoin d’une image appelée un glyphe afin d’indiquer à
l’utilisateur la fonction du bouton. Si vous ne spécifiez qu’une seule image pour
le bouton, le bouton manipule l’image afin d’indiquer si le bouton est enfoncé,
relâché, sélectionné ou désactivé. Vous pouvez également spécifier des images
distinctes spécifiques à chaque état.
Normalement, les glyphes sont affectés à un turbobouton à la conception mais il
est possible d’affecter d’autres glyphes à l’exécution.
Pour affecter un glyphe à un turbobouton à la conception :
1 Sélectionnez le turbobouton.
2 Dans l’inspecteur d’objets, sélectionnez la propriété Glyph.
3 Double-cliquez dans la colonne des valeurs à côté de Glyph pour afficher
l’éditeur d’images et sélectionner le bitmap souhaité.

Définition de l’état initial d’un turbobouton


C’est l’aspect visuel d’un turbobouton qui donne à l’utilisateur des indications
sur sa fonction et son état. N’ayant pas d’intitulé, il est indispensable d’utiliser
des indications visuelles pour aider l’utilisateur.

Conception de l’interface utilisateur des applications 5-33


Conception de barres d’outils et de barres multiples

Le tableau suivant décrit comment modifier l’aspect d’un turbobouton :


Tableau 5.3 Paramétrage de l’aspect d’un turbobouton
Pour que le turbobouton : Initialisez :
Apparaisse enfoncé Sa propriété GroupIndex à une valeur non nulle et sa
propriété Down à True.
Apparaisse désactivé Sa propriété Enabled à False.
Dispose d’une marge gauche Sa propriété Indent avec une valeur supérieure à 0.

Si, par exemple, votre application propose un outil de dessin activé par défaut,
vérifiez que le bouton correspondant de la barre d’outils est enfoncé au
démarrage de l’application. Pour ce faire, affectez une valeur non nulle à sa
propriété GroupIndex et la valeur True à sa propriété Down.

Création d’un groupe de turboboutons


Une série de turboboutons représente souvent un ensemble de choix
mutuellement exclusifs. Dans ce cas, vous devez associer les boutons dans un
groupe, afin que l’enfoncement d’un bouton fasse remonter le bouton
précédemment enfoncé du groupe.
Pour associer des turboboutons dans un groupe, affectez la même valeur à la
propriété GroupIndex de chacun des turboboutons.
Le moyen le plus simple de procéder consiste à sélectionner tous les boutons à
grouper puis à spécifier une même valeur pour leur propriété GroupIndex.

Utilisation de boutons bascule


Dans certains cas, vous voulez pouvoir cliquer sur un bouton déjà enfoncé d’un
groupe afin de le faire remonter, ce qui laisse le groupe sans aucun bouton
enfoncé. Un tel bouton est appelé une bascule. Utilisez la propriété AllowAllUp
pour créer un groupe de boutons qui se comporte ainsi : cliquez une fois; il est
enfoncé, cliquez à nouveau, il remonte.
Pour qu’un groupe de boutons radio se comporte comme une bascule, affectez à
sa propriété AllowAllUp la valeur True.
L’affectation de la valeur True à la propriété AllowAllUp d’un des turboboutons
du groupe l’affecte à tous ceux du groupe. Cela permet au groupe de se
comporter comme un groupe normal, un seul bouton étant sélectionné à la fois
mais, au même moment, tous les boutons peuvent être à l’état relâché.

Ajout d’une barre d’outils en utilisant le composant


barre d’outils
Le composant barre d’outils (TToolBar) propose des caractéristiques de gestion
des boutons et de l’affichage dont ne dispose pas le composant volet. Pour
ajouter une barre d’outils à une fiche en utilisant le composant barre d’outils :
1 Ajoutez à la fiche un composant barre d’outils de la page Win32 de la palette
des composants. La barre d’outils s’aligne automatiquement en haut de la fiche.

5-34 Guide du développeur


Conception de barres d’outils et de barres multiples

2 Ajoutez des boutons outils ou d’autres contrôles à la barre.


Les boutons outils sont conçus pour fonctionner dans des composants barre
d’outils. Comme les turboboutons, les boutons outils peuvent :
• Se comporter comme des boutons poussoirs normaux.
• Se comporter comme des bascules.
• Se comporter comme un ensemble de boutons radio.
Pour implémenter des boutons outils dans une barre d’outils, vous pouvez :
• Ajouter un bouton outil
• Affecter des images à un bouton outil
• Définir l’aspect et les conditions initiales du bouton outil
• Créer un groupe de boutons outils
• Utiliser des boutons outils bascule

Ajout d’un bouton outil


Pour ajouter un bouton outil dans une barre d’outils, cliquez avec le bouton
droit de la souris dans la barre d’outils et choisissez Nouveau bouton.
La barre d’outils est le "propriétaire" du bouton outil : déplacer ou masquer la
barre d’outils déplace et masque également le bouton. De plus, tous les boutons
outils de la barre d’outils ont automatiquement la même largeur et la même
hauteur. Vous pouvez déposer dans la barre d’outils d’autres contrôles de la
palette des composants, ils ont automatiquement une hauteur homogène. De
plus, les contrôles passent à la ligne et commencent une nouvelle ligne quand ils
ne tiennent pas horizontalement sur une seule ligne de la barre d’outils.

Affectation d’images à des boutons outils


Chaque bouton outil dispose de la propriété ImageIndex qui détermine l’image
apparaissant dedans à l’exécution. Si vous ne fournissez qu’une seule image au
bouton outil, le bouton manipule cette image pour indiquer si le bouton est
désactivé. Pour affecter une image à un bouton outil à la conception :
1 Sélectionnez la barre d’outils dans laquelle le bouton apparaît.
2 Dans l’inspecteur d’objets, affectez un objet TImageList à la propriété Images de
la barre d’outils (une liste d’images est une collection d’icônes ou de bitmaps
de même taille).
3 Sélectionnez un bouton outil.
4 Dans l’inspecteur d’objets, affectez à la propriété ImageIndex du bouton outil
une valeur entière correspondant à l’image de la liste d’images qui doit être
affectée au bouton.
Vous pouvez également spécifier des images distinctes apparaissant dans les
boutons outils quand ils sont désactivés ou sous le pointeur de la souris. Pour ce
faire, affectez des listes d’images distinctes aux propriétés DisabledImages et
HotImages de la barre d’outils.

Conception de l’interface utilisateur des applications 5-35


Conception de barres d’outils et de barres multiples

Définition de l’aspect et de l’état initial d’un bouton outil


Le tableau suivant décrit comment modifier l’aspect d’un bouton outil :
Tableau 5.4 Paramétrage de l’aspect d’un bouton outil
Pour que le bouton outil : Initialisez :
Apparaisse enfoncé Sa propriété GroupIndex à une valeur non nulle et
sa propriété Down à True.
Apparaisse désactivé Sa propriété Enabled à False.
Dispose d’une marge gauche Sa propriété Indent à une valeur supérieure à 0.
Semble avoir une bordure “pop-up”, Sa propriété Flat à True.
ce qui donne à la barre d’outils un
aspect transparent

Remarque L’utilisation de la propriété Flat de TToolBar nécessite une version 4.70 ou


ultérieure de COMCTL32.DLL.
Pour forcer le passage à la ligne après un bouton outil spécifique, sélectionnez le
bouton outil devant apparaître en dernier sur la ligne et affectez la valeur True à
sa propriété Wrap.
Pour désactiver le passage à la ligne automatique dans la barre d’outils, affectez
la valeur False à la propriété Wrapable de la barre d’outils.

Création de groupes de boutons outils


Pour créer un groupe de boutons outils, sélectionnez les boutons à associer et
affectez la valeur tbsCheck à leur propriété Style et la valeur True à leur propriété
Grouped. La sélection d’un bouton outil du groupe désélectionne le choix
précédent dans le groupe de boutons, ce qui permet de représenter un ensemble
de choix mutuellement exclusifs.
Toute séquence non interrompue de boutons outils adjacents dont la propriété
Style a la valeur tbsCheck et la propriété Grouped la valeur True forme un même
groupe. Pour interrompre un groupe de boutons outils, séparez les boutons avec :
• Un bouton outil dont la propriété Grouped a la valeur False.
• Un bouton outil dont la propriété Style n’a pas la valeur tbsCheck. Pour créer
des espaces ou des séparateurs dans la barre d’outils, ajoutez un bouton outil
de Style tbsSeparator ou tbsDivider.
• Un contrôle d’un type autre que bouton outil.

Utilisation de boutons outils bascule


Utilisez la propriété AllowAllUp pour créer un groupe de boutons outils se
comportant comme une bascule : cliquez une fois pour enfoncer le bouton et une
seconde fois pour le faire remonter. Pour qu’un groupe de boutons outils se
comporte comme une bascule, affectez la valeur True à sa propriété AllowAllUp.
Comme pour les turboboutons, l’affectation de la valeur True à la propriété
AllowAllUp d’un des boutons du groupe affecte automatiquement la même
valeur à tous les boutons du groupe.

5-36 Guide du développeur


Conception de barres d’outils et de barres multiples

Ajout d’un composant barre multiple


Le composant barre multiple affiche des contrôles fenêtrés dans des bandes
redimensionnables qui peuvent se déplacer indépendamment les unes des autres.
L’utilisateur peut positionner les bandes faisant glisser des poignées de
redimensionnement placées sur le côté de chaque bande.
Pour utiliser une barre multiple comme barre d’outils dans une fiche :
1 Ajoutez à la fiche un composant barre multiple de la page Win32 de la palette
des composants. La barre multiple s’aligne automatiquement en haut de la
fiche.
2 Ajoutez à la barre des contrôles fenêtrés de la palette des composants.
Seuls les composants dérivant de TWinControl sont des contrôles fenêtrés. Vous
pouvez ajouter à la barre multiple des contrôles graphiques (comme les libellés
ou les turboboutons), mais ils n’apparaissent pas dans des bandes distinctes.
Remarque Le composant barre multiple nécessite une version 4.70 ou ultérieure de
COMCTL.DLL.

Initialisation de l’aspect de la barre multiple


Le composant barre multiple dispose de plusieurs options utiles pour la
configuration. Le tableau suivant indique comment modifier l’aspect d’une barre
multiple :

Tableau 5.5 Paramétrage de l’aspect d’une barre multiple


Pour que la barre multiple : Initialisez :
Se redimensionne automatiquement pour Sa propriété AutoSize à True
s’adapter aux bandes qu’elle contient.
Dispose de bandes d’une hauteur Sa propriété FixedSize à True.
uniforme.
Soit orientée à la verticale et pas à Sa propriété Vertical à True. Cela change l’effet de
l’horizontale. la propriété FixedSize.
Empêcher l’affichage à l’exécution de la Sa propriété ShowText à False. Chaque bande
propriété Text des bandes. d’une barre multiple a sa propre propriété Text.
Retirer la bordure autour de la barre. Sa propriété BandBorderStyle à bsNone
Empêcher les utilisateurs de modifier Sa propriété FixedOrder à True
l’ordre des bandes à l’exécution.
L’utilisateur peut toujours déplacer et
redimensionner les bandes.
Créer une image de fond pour la barre Sa propriété Bitmap avec un objet TBitmap
multiple.
Choisir une liste d’images apparaissant à Sa propriété Images avec un objet TImageList
gauche des bandes

Pour affecter individuellement des images aux bandes, sélectionnez la barre


multiple et, dans l’inspecteur d’objets, double-cliquez sur sa propriété Bands.
Sélectionnez une bande et affectez une valeur à sa propriété ImageIndex.

Conception de l’interface utilisateur des applications 5-37


Conception de barres d’outils et de barres multiples

Réponse aux clics


Quand l’utilisateur clique sur un contrôle, par exemple un bouton d’une barre
d’outils, l’application génère un événement OnClick auquel vous pouvez
répondre avec un gestionnaire d’événement. Etant donné que OnClick est
l’événement par défaut des boutons, vous pouvez générer un gestionnaire
squelette pour l’événement en double-cliquant sur le bouton à la conception.
Pour plus d’informations, voir “Utilisation des événements et des gestionnaires
d’événements” à la page 2-28 et “Génération du gestionnaire de l’événement par
défaut d’un composant” à la page 2-28.

Affectation d’un menu à un bouton outil


Si vous utilisez une barre d’outils (TToolBar) contenant des boutons outils
(TToolButton), vous pouvez associer un menu à un bouton spécifique :
1 Sélectionnez le bouton outil.
2 Dans l’inspecteur d’objets, affectez un menu surgissant (TPopupMenu) à la
propriété DropDownMenu du bouton outil.
Si la propriété AutoPopup du menu a la valeur True, le menu apparaît
automatiquement quand le bouton est enfoncé.

Ajout de barres d’outils masquées


Les barres d’outils n’ont pas besoin d’être visibles tout le temps. En fait, il est
souvent commode de disposer de plusieurs barres d’outils, mais de n’afficher
que celles dont l’utilisateur veut disposer. Souvent, les fiches que vous créez
contiennent plusieurs barres d’outils, mais en masquent certaines ou même
toutes.
Pour créer une barre d’outils masquée :
1 Ajoutez à la fiche un composant barre d’outils, barre multiple ou volet.
2 Affectez la valeur False à la propriété Visible du composant.
Bien que la barre d’outils reste visible à la conception afin que vous puissiez la
modifier, elle reste cachée à l’exécution tant que l’application ne la rend pas
explicitement visible.

Affichage d’une barre d’outils


Fréquemment, une application dispose de plusieurs barres d’outils mais vous ne
voulez pas encombrer l’écran en les affichant toutes à la fois. Vous pouvez
laisser l’utilisateur décider s’il veut afficher les barres d’outils. Comme tous les
composants, les barres d’outils peuvent être masquées et affichées quand c’est
nécessaire à l’exécution.

5-38 Guide du développeur


Utilisation des listes d’actions

Pour masquer ou afficher une barre d’outils à l’exécution, affectez à sa propriété


Visible, respectivement, la valeur False ou True. Généralement vous faites ceci en
réponse à un événement utilisateur particulier ou à un changement du mode de
fonctionnement de l’application. Pour ce faire, chaque barre d’outils dispose
généralement d’un bouton de fermeture. Quand l’utilisateur clique sur ce bouton,
l’application masque la barre d’outils correspondante.
Vous pouvez également proposer un système pour inverser l’état de la barre
d’outils. Dans l’exemple suivant, la visibilité d’une barre d’outils de crayons est
inversée par un bouton de la barre d’outils principale. Comme chaque clic de la
souris enfonce ou libère le bouton, un gestionnaire d’événement OnClick peut
afficher ou masquer la barre d’outils des crayons selon que le bouton est relâché
ou enfoncé.
procedure TForm1.PenButtonClick(Sender: TObject);
begin
PenBar.Visible := PenButton.Down;
end;

Utilisation des listes d’actions


Les listes d’actions permettent de centraliser la réponse à des commandes
utilisateur (des actions) pour des objets comme les menus et les boutons qui
répondent à ces commandes. Cette section présente les actions et les listes
d’actions et décrit comment les utiliser et comment elles interagissent avec leurs
clients et leurs cibles.

Objets action
Les actions sont des commandes utilisateur portant sur des objets cible. Les
actions sont créées dans l’éditeur de composant liste d’actions. Ces actions sont
ultérieurement connectées à des contrôles client via leur liaison d’action. Le
mécanisme action/liste d’actions fait intervenir les composants suivants :
• Une action (TAction) est l’implémentation d’une action (par exemple, copier le
texte sélectionné) dans une cible (par exemple, un contrôle de saisie). Une
action est déclenchée par un client en réponse à une commande de
l’utilisateur (c’est-à-dire un clic de la souris). Les clients sont habituellement
des éléments de menu ou des boutons. L’unité StdActns contient des classes,
dérivées de TAction, qui implémentent les commandes de menu (les actions)
standard d’édition et de manipulation des fenêtres qui apparaissent dans la
plupart des applications Windows.
• Une liste d’actions (TActionList) est un composant qui gère une liste d’actions
(TAction). Les listes d’actions servent à la conception d’interfaces utilisateur
permettant de manipuler des actions.
• Une liaison d’action (TActionLink) est un objet qui gère la connexion entre les
actions et les clients. Les liaisons d’actions déterminent si une action, et
laquelle, est actuellement applicable à un client donné.

Conception de l’interface utilisateur des applications 5-39


Utilisation des listes d’actions

• Le client d’une action est habituellement un élément de menu ou un bouton


(TToolButton, TSpeedButton, TMenuItem, TButton, TCheckBox, TRadioButton, etc).
Une action est initiée par une commande correspondante dans le client.
Généralement un Click client est associé à l’Execute d’une action.
• La cible d’une action est généralement un contrôle, par exemple un éditeur de
texte formaté, un mémo ou un contrôle de données. L’unité DBActns, par
exemple, contient des classes qui implémentent des actions spécifiques aux
contrôles ensemble de données. Les concepteurs de composants peuvent créer
des actions spécifiques aux besoins des contrôles qu’ils conçoivent et utilisent,
puis empaqueter ces unités pour créer des applications plus modulaires.
La figure suivante illustre les relations entre ces objets. Dans ce diagramme, Cut1
est l’action, ActionList1 est la liste d’actions contenant Cut1, SpeedButton1 est le
client de Cut1 et Memo1 est la cible. A la différence des actions, des listes
d’actions, des clients d’actions et des cibles d’actions, les liaisons d’actions sont
des objets non visuels. La liaison d’action est donc indiquée dans ce diagramme
par un rectangle blanc. La liaison d’action relie ensemble le client SpeedButton1 et
l’action Cut1 contenue dans ActionList1.
Figure 5.10 Mécanismes d’une liste d’action

La VCL contient des classes dérivées des types TAction, TActionList et


TActionLink utilisées par le mécanisme de liste d’actions. Regroupées par unité,
ce sont :
• ActnList.pas : TAction, TActionLink, TActionList, TContainedAction,
TCustomAction et TCustomActionList.
• Classes.pas : TBasicAction et TBasicActionLink.
• Controls.pas : TControlActionLink et TWinControlActionLink.
• ComCtrls.pas : TToolButtonActionLink.
• Menus.pas : TMenuActionLink.
• StdCtrls.pas : TButtonActionLink.

5-40 Guide du développeur


Utilisation des listes d’actions

Deux autres unités, StdActns et DBActns, contiennent des classes auxiliaires qui
implémentent des actions spécifiques courantes de Windows et des ensembles de
données. Pour davantage d’informations, voir “Classes d’actions prédéfinies” à la
page 5-44. La plupart des contrôles de la VCL proposent des propriétés (c’est-à-
dire Action) et des méthodes (c’est-à-dire ExecuteAction) qui permettent de les
utiliser comme client ou comme cible d’actions.

Utilisation des actions


Vous pouvez ajouter à vos fiches et modules de données une liste d’actions de la
page Standard de la palette des composants. Double-cliquez sur la liste d’actions
pour afficher l’éditeur de liste d’actions qui vous permet d’ajouter, de supprimer
et de réorganiser des actions de la même manière que vous utilisez un éditeur
de collection.
Dans l’inspecteur d’objets, définissez les propriétés de chaque action. La
propriété Name identifie l’action, les autres propriétés (Caption, Checked, Enabled,
HelpContext, Hint, ImageIndex, ShortCut et Visible) correspondent aux propriétés
des contrôles client. Elles portent généralement, mais pas obligatoirement, le
même nom que la propriété du client. Ainsi, la propriété Checked d’une action
correspond à la propriété Down d’un contrôle TToolButton.

Centralisation du code
De nombreux contrôles, par exemple TToolButton, TSpeedButton, TMenuItem ou
TButton ont une propriété publiée nommée Action. Quand vous affectez à la
propriété Action l’une des actions de votre liste d’actions, les valeurs des
propriétés correspondantes de l’action sont copiées dans celles du contrôle.
Toutes les propriétés et événements en commun avec l’objet action (sauf Name et
Tag) sont liées dynamiquement au contrôle. Ainsi, par exemple, au lieu de
dupliquer le code désactivant des boutons et des éléments de menu, vous
pouvez centraliser ce code dans un objet action : quand l’action est désactivée,
tous les boutons et les éléments de menu correspondants sont désactivés.

Liaison de propriétés
La liaison d’action du client est le mécanisme par lequel ses propriétés sont
associées (liées) aux propriétés d’une action. Quand une action change, la liaison
d’action est responsable de l’actualisation des propriétés du client. Pour
davantage d’informations sur les propriétés gérées par une classe donnée de
liaison d’action, voir les diverses classes de liaison d’action dans l’aide en ligne
de la VCL.
Vous pouvez surcharger de manière sélective les valeurs des propriétés
contrôlées par un objet action associé en initialisant la valeur de la propriété
dans le composant ou le contrôle client. Cela ne modifie pas la valeur de la
propriété dans l’action et seul le client est affecté.

Conception de l’interface utilisateur des applications 5-41


Utilisation des listes d’actions

Exécution d’actions
Quand un composant ou un contrôle client est cliqué, l’événement OnExecute se
produit pour son action associée. Par exemple, le code suivant est le gestionnaire
d’événement OnExecute pour une action qui inverse la visibilité d’une barre
d’outils quand une action est exécutée :
procedure TForm1.Action1Execute(Sender: TObject);
begin
{ Inverse la visibilité de Toolbar1 }
ToolBar1.Visible := not ToolBar1.Visible;
end;
Remarque Si vous utilisez un bouton outil ou un élément de menu, vous devez initialiser
manuellement la propriété Images de la barre d’outils ou du menu correspondant
avec la propriété Images de la liste d’actions. Cela reste vrai même si la propriété
ImageIndex est liée dynamiquement au client.
Pour des informations générales sur les événements et les gestionnaires
d’événements, voir “Utilisation des événements et des gestionnaires
d’événements” à la page 2-28.
La figure suivante illustre la séquence de répartition du cycle d’exécution d’une
action nommée Cut1. Ce diagramme part de la relation illustrée par la
figure 5.10, ce qui signifie que le client Speedbutton1 est lié à l’action Cut1 via sa
liaison d’action. La propriété Action de Speedbutton1 a donc la valeur Cut1. En
conséquence, la méthode Click de Speedbutton1 appelle la méthode Execute de
Cut1.
Figure 5.11 Cycle d’exécution d’une action

5-42 Guide du développeur


Utilisation des listes d’actions

Remarque Dans la description de cette séquence, l’appel d’une méthode par une autre ne
signifie pas nécessairement que l’appel apparaît explicitement dans le code de la
méthode.
Cliquer sur Speedbutton1 démarre le cycle d’exécution suivant :
• La méthode Click de Speedbutton1 appelle Cut1.Execute.
• L’action Cut1 s’en remet à sa liste d’actions (ActionList1) pour le traitement de
son Execute. Elle le fait en appelant la méthode ExecuteAction de la liste
d’actions en se transmettant elle-même comme paramètre.
• ActionList1 appelle son gestionnaire d’événement (OnExecuteAction) pour
ExecuteAction (la méthode ExecuteAction d’une liste d’actions s’applique à
toutes les actions contenues dans la liste). Ce gestionnaire a un paramètre
Handled qui renvoie False par défaut. Si le gestionnaire est défini et gère
l’événement, il doit renvoyer True et la séquence de traitement s’arrête là.
Par exemple :
procedure TForm1.ActionList1ExecuteAction(Action: TBasicAction; var Handled: Boolean);
begin
{ Empêche l’exécution des actions contenues par ActionList1 }
Handled := True;
end;
Si l’exécution n’est pas gérée à ce point dans le gestionnaire de la liste d’actions,
le traitement se poursuit :
• L’action Cut1 est redirigée vers la méthode ExecuteAction de l’objet Application,
qui appelle le gestionnaire OnExecuteAction (la méthode ExecuteAction de
l’application s’applique à toutes les actions de cette application). La séquence
est la même que pour le ExecuteAction de la liste d’actions : le gestionnaire a
un paramètre Handled qui renvoie False par défaut. Si le gestionnaire est défini
et gère l’événement, il doit renvoyer True et la séquence de traitement s’arrête
ici. Par exemple :
procedure TForm1.ApplicationExecuteAction(Action: TBasicAction; var Handled: Boolean);
begin
{ Empêche l’exécution de toutes les actions de Application }
Handled := True;
end;
Si l’exécution n’est pas gérée par le gestionnaire d’événement de l’application,
alors Cut1 envoie le message CM_ACTIONEXECUTE à la méthode WndProc de
l’application en se transmettant elle-même comme paramètre. L’application tente
alors de trouver une cible sur laquelle exécuter l’action (voir la figure 5.12,
“Cibles des actions”).

Conception de l’interface utilisateur des applications 5-43


Utilisation des listes d’actions

Actualisation des actions


Quand l’application est inactive, l’événement OnUpdate se produit pour chaque
action liée à un contrôle visible ou un élément de menu affiché. Cela permet aux
applications d’exécuter du code centralisé pour activer ou désactiver, cocher ou
retirer les marques de sélection, etc. Par exemple, le code suivant est le
gestionnaire d’événement OnUpdate d’une liste d’actions qui est “cochée” quand
la barre d’outils est visible :
procedure TForm1.Action1Update(Sender: TObject);
begin
{ Indique si ToolBar1 est actuellement visible }
(Sender as TAction).Checked := ToolBar1.Visible;
end;
Pour un autre exemple, voir le programme exemple RichEdit (Demos\RichEdit).
Le cycle de répartition de l’actualisation des actions suit la même séquence que
le cycle d’exécution de la figure 5.11.
Remarque Ne placez pas de code nécessitant une exécution longue dans le gestionnaire
d’événement OnUpdate. En effet, il est exécuté à chaque fois que l’application est
inactive. Si l’exécution de ce gestionnaire d’événement est trop longue, les
performances de toute l’application s’en trouvent affectées.

Classes d’actions prédéfinies


Les concepteurs de composants peuvent utiliser comme exemple les classes
définies dans les unités StdActns et DBActns afin de dériver leurs propres classes
action qui implémentent des comportements spécifiques à leurs contrôles et leurs
composants. Les classes de base de ces actions spécialisées (TEditAction,
TWindowAction) surchargent généralement HandlesTarget, UpdateTarget et d’autres
méthodes afin de limiter la cible de l’action à une classe spécifique d’objets. Les
classes dérivées surchargent habituellement ExecuteTarget pour réaliser une tâche
spécifique.

Actions standard d’édition


Les actions standard d’édition sont conçues pour être utilisées avec une cible
contrôle de saisie. TEditAction est la classe de base pour des descendants qui
doivent surcharger la méthode ExecuteTarget afin d’implémenter les opérations
copier, couper et coller en utilisant le Presse-papiers Windows.
• TEditAction vérifie que le contrôle cible est une classe TCustomEdit (ou une
classe en dérivant).
• TEditCopy copie le texte mis en évidence dans le Presse-papiers.
• TEditCut coupe dans le Presse-papiers le texte mis en évidence dans la cible.
• TEditPaste colle le texte du Presse-papiers dans la cible et vérifie que le Presse-
papiers utilise le format texte.

5-44 Guide du développeur


Utilisation des listes d’actions

• TEditDelete supprime le texte mis en évidence.


• TEditSelectAll sélectionne tout le texte du contrôle d’édition cible.
• TEditUndo annule la dernière modification effectuée au contrôle d’édition cible.

Actions standard de fenêtre


Les actions standard des fenêtres sont conçues pour être utilisées, avec comme
cible, les fiches d’une application MDI. TWindowAction est la classe de base pour
des descendants qui surchargent la méthode ExecuteTarget pour implémenter la
réorganisation, la cascade, la fermeture, la mosaïque et la réduction de fenêtres
enfant MDI.
• TWindowAction vérifie que le contrôle cible est une classe TForm et teste si la
fiche a des fiches enfant MDI.
• TWindowArrange réorganise les icônes des fiches enfant MDI réduites.
• TWindowCascade affiche en cascade les fiches enfant MDI
• TWindowClose ferme la fiche enfant MDI active.
• TWindowMinimizeAll réduit toutes les fiches enfant MDI.
• TWindowTileHorizontal dispose les fiches enfant MDI en une mosaïque
horizontale.
• TWindowTileVertical dispose les fiches enfant MDI en une mosaïque verticale.

Actions d’aide standard


Les actions d’aide standard sont conçues pour une utilisation avec toute cible.
THelpAction est la classe de base pour les descendants dont chacun redéfinit la
méthode ExecuteTarget pour transmettre les commandes à WinHelp.
• THelpAction garantit que la variable Application globale est disponible, afin que
les commandes puissent être gérées au moyen de sa méthode HelpCommand.
• THelpContents amène la boîte de dialogue des rubriques d’aide sur le dernier
onglet utilisé (Sommaire de l’aide, Index ou Rechercher).
• THelpTopicSearch amène la boîte de dialogue des rubriques d’aide sur l’onglet
Index.
• THelpOnHelp amène le fichier d’aide Microsoft sur la rubrique d’utilisation de
l’aide. Remarquez que ce fichier est un fichier d’aide HTML sur les récentes
versions de Windows qui ne décrit pas le système WinHelp.

Actions des ensembles de données


Les actions standard des ensembles de données sont conçues pour être utilisées
avec un composant ensemble de données comme cible. TDataSetAction est la
classe de base de descendants qui surchargent les méthodes ExecuteTarget et
UpdateTarget afin d’implémenter les déplacements et les éditions de la cible.

Conception de l’interface utilisateur des applications 5-45


Utilisation des listes d’actions

La classe TDataSetAction ajoute la propriété DataSource qui garantit que les


actions sont effectuées sur l’ensemble de données. Si DataSource a la valeur nil,
le contrôle orienté données détenant la focalisation est utilisé. Pour davantage
d’informations, voir la figure 5.12, “Cibles des actions,” à la page 5-47.
• TDataSetAction vérifie que la cible est une classe TDataSource ayant un
ensemble de données associé.
• TDataSetCancel annule les saisies de l’enregistrement en cours, rétablit
l’affichage de l’enregistrement à ce qu’il était avant la saisie et désactive les
états insertion et modifications s’ils sont actifs.
• TDataSetDelete supprime l’enregistrement en cours et active l’enregistrement
suivant.
• TDataSetEdit place l’ensemble de données à l’état modification afin de pouvoir
modifier l’enregistrement en cours.
• TDataSetFirst rend actif le premier enregistrement de l’ensemble de données.
• TDataSetInsert insère un nouvel enregistrement avant l’enregistrement en cours
et place l’ensemble de données dans les états insertion et modification.
• TDataSetLast rend actif le dernier enregistrement de l’ensemble de données.
• TDataSetNext rend actif l’enregistrement suivant de l’ensemble de données.
• TDataSetPost écrit dans l’ensemble de données les modifications de
l’enregistrement en cours.
• TDataSetPrior rend actif l’enregistrement précédent de l’ensemble de données.
• TDataSetRefresh rafraîchit les données du tampon avec celles de l’ensemble de
données associé.

Conception de composants utilisant des actions


Les actions prédéfinies sont des exemples illustrant la manière d’étendre les
classes action de la VCL. Les rubriques suivantes décrivent comment créer vos
propres classes action :
• Comment les actions trouvent leurs cibles
• Recensement d’actions
• Ecriture d’éditeurs de listes d’actions

Comment les actions trouvent leurs cibles


La figure 5.11 illustre le cycle d’exécution des classes action standard de la VCL.
Si l’exécution n’est pas gérée par la liste d’actions, par l’application ou par les
gestionnaires d’événements action par défaut, alors le message
CM_ACTIONEXECUTE est envoyé à la méthode WndProc de l’application. La
figure 5.12poursuit la séquence d’exécution une fois arrivé à ce point.

5-46 Guide du développeur


Utilisation des listes d’actions

Les classes d’actions prédéfinies décrites plus haut, mais aussi les classes
d’actions que vous créez utilisent cette séquence d’exécution :
Figure 5.12 Cibles des actions

• Au moment où elle reçoit le message CM_ACTIONEXECUTE, l’application


commence par le redistribuer à la fiche active (ActiveForm) à l’écran. S’il n’y a
pas de fiche active, l’application envoie le message à sa fiche principale
(MainForm).
• Form1 (la fiche active dans cet exemple) commence par chercher le contrôle
actif (Memo1) et appelle sa méthode ExecuteAction en lui transmettant Cut1
comme paramètre.
• Memo1 appelle la méthode HandlesTarget de Cut1 en se transmettant lui-même
comme paramètre afin de déterminer s’il est une cible appropriée pour
l’action. Si Memo1 n’est pas une cible appropriée, HandlesTarget renvoie False et
le gestionnaire ExecuteAction de Memo1 renvoie False.
• Dans cet exemple, Memo1 est une cible appropriée pour Cut1, donc
HandlesTarget renvoie True. Memo1 appelle ensuite Cut1.ExecuteTarget en se
transmettant lui-même comme paramètre. Enfin, comme Cut1 est une instance
de l’action TEditCut, action appelle la méthode CutToClipoard de Memo1. :
procedure TEditCut.ExecuteTarget(Target: TObject);
begin
GetControl(Target).CutToClipboard;
end;
Si le contrôle n’est pas une cible appropriée ; le traitement se poursuit de la
manière suivante :
• Form1 appelle sa méthode ExecuteAction. Si Form1 est une cible appropriée
(une fiche peut, par exemple, être la cible de l’action TWindowCascade), alors la
fiche appelle la méthode Cut1.ExecuteTarget en se transmettant elle-même
comme paramètre.

Conception de l’interface utilisateur des applications 5-47


Utilisation des listes d’actions

• Si Form1 n’est pas une cible appropriée, elle appelle ExecuteAction pour chacun
des contrôles visibles dont elle est propriétaire jusqu’à trouver une cible.
Remarque Si l’action impliquée est de type TCustomAction, l’action est automatiquement
désactivée quand l’action n’est pas gérée si sa propriété DisableIfNoHandler a la
valeur True.

Recensement d’actions
Vous pouvez recenser ou annuler le recensement de vos propres actions dans
l’EDI en utilisant les routines globales de l’unité ActnList :
procedure RegisterActions(const CategoryName: string; const AClasses: array of
TBasicActionClass; Resource: TComponentClass);
procedure UnRegisterActions(const AClasses: array of TBasicActionClass);
Utilisez ces routines de la même manière que RegisterComponents s’utilise pour
recenser des composants. Par exemple, le code suivant recense dans l’EDI les
actions :
{ recensement des actions standard }
RegisterActions('', [TAction], nil);
RegisterActions('Edit', [TEditCut, TEditCopy, TEditPaste], TStandardActions);
RegisterActions('Window', [TWindowClose, TWindowCascade, TWindowTileHorizontal,
TWindowTileVertical, TWindowMinimizeAll, TWindowArrange], TStandardActions);

Ecriture d’éditeurs de listes d’actions


Vous pouvez écrire vos propres composants éditeur pour les listes d’actions. Si
c’est le cas, vous pouvez affecter vos propres procédures aux quatre variables
globales procédure de l’unité ActnList :
CreateActionProc: function (AOwner: TComponent; ActionClass: TBasicActionClass):
TBasicAction = nil;
EnumRegisteredActionsProc: procedure (Proc: TEnumActionProc; Info: Pointer) = nil;
RegisterActionsProc: procedure (const CategoryName: string; const AClasses: array of
TBasicActionClass; Resource: TComponentClass) = nil;
UnRegisterActionsProc: procedure (const AClasses: array of TBasicActionClass) = nil;
Vous n’avez besoin de réaffecter ces variables procédures que si vous voulez
gérer différemment les procédures de recensement, d’annulation du recensement,
de création et d’énumération des actions. Si c’est le cas, écrivez vos propres
gestionnaires et affectez-les à ces variables dans la section initialisation de votre
unité de conception.

Programmes exemple
Pour des exemples de programmes utilisant les actions et les listes d’actions, voir
Demos\RichEdit. De plus, les démos de l’expert Application (Fichier|page
Nouveau projet), MDI Application, SDI Application et Win95 Logo Application
peuvent utiliser les objets action et liste d’actions.

5-48 Guide du développeur


Chapitre

Manipulation des contrôles


Chapter 6
6
Les contrôles sont des composants visuels avec lesquels l’utilisateur peut
interagir à l’exécution. Ce chapitre décrit un ensemble de fonctionnalités
communes à de nombreux contrôles.

Implémentation du glisser-déplacer dans les


contrôles
Le glisser-déplacer est souvent une façon pratique de manipuler des objets. Les
utilisateurs peuvent ainsi faire glisser des contrôles entiers, ou bien extraire des
éléments de contrôles (tels que des boîtes liste ou des vues arborescentes) en les
faisant glisser sur d’autres contrôles.
• Début de l’opération glisser-déplacer
• Acceptation des éléments à déplacer
• Déplacement des éléments
• Fin de l’opération glisser-déplacer
• Personnalisation du glisser-déplacer avec un objet déplacement
• Changement du pointeur de la souris

Début de l’opération glisser-déplacer


Chaque contrôle possède une propriété appelée DragMode qui détermine la façon
dont les opérations glisser sont démarrées. Si la propriété DragMode est à
dmAutomatic, l’opération glisser commence automatiquement quand l’utilisateur
clique sur le bouton de la souris alors que le curseur se trouve au-dessus d’un
contrôle. Le plus souvent, vous donnerez à DragMode la valeur dmManual (la
valeur par défaut) et lancerez l’opération glisser en gérant les événements bouton
de souris enfoncé.

Manipulation des contrôles 6-1


Implémentation du glisser-déplacer dans les contrôles

Pour faire glisser un contrôle manuellement, appelez la méthode BeginDrag du


contrôle. BeginDrag requiert un paramètre booléen appelé Immediate. Si vous
transmettez True, l’opération glisser commence immédiatement. Si vous
transmettez False, l’opération glisser ne commence pas avant que l’utilisateur ne
déplace la souris. L’appel de BeginDrag(False) permet au contrôle d’accepter les
clics de la souris sans lancer une opération glisser.
Vous pouvez imposer des conditions pour commencer l’opération glisser, par
exemple vérifier le bouton de souris enfoncé par l’utilisateur, en testant les
paramètres du gestionnaire de l’événement bouton de souris enfoncé, avant
l’appel à BeginDrag. Le code qui suit, par exemple, gère l’événement bouton de
souris enfoncé dans une boîte liste de fichiers en ne lançant l’opération glisser
que si le bouton gauche de la souris a été enfoncé.
procedure TFMForm.FileListBox1MouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Button = mbLeft then { ne glisser que si le bouton gauche est enfoncé }
with Sender as TFileListBox do { traite Sender comme TFileListBox }
begin
if ItemAtPos(Point(X, Y), True) >= 0 then { y a-t-il un élément ici ? }
BeginDrag(False); { si oui, le faire glisser }
end;
end;

Acceptation des éléments à déplacer


Quand l’utilisateur fait glisser quelque chose sur un contrôle, celui-ci reçoit un
événement OnDragOver. Il doit alors indiquer s’il peut accepter l’élément dans le
cas où l’utilisateur le lâcherait à cet emplacement. L’aspect du curseur change
pour indiquer si le contrôle peut accepter l’élément que l’utilisateur fait glisser.
Pour accepter les éléments que l’utilisateur fait glisser sur un contrôle, attachez
un gestionnaire à l’événement OnDragOver du contrôle.
L’événement “glisser-dessus” a un paramètre appelé Accept que le gestionnaire
d’événement peut définir à True pour indiquer qu’il accepte l’élément. Si Accept
vaut True, l’application renvoie un événement “glisser-déplacer” au contrôle.
L’événement “glisser-dessus” présente d’autres paramètres, dont la source de
l’opération glisser et l’emplacement actuel du curseur de la souris, que le
gestionnaire d’événement peut utiliser pour déterminer s’il doit accepter le
déplacement. Dans l’exemple ci-dessous, une arborescence de répertoires accepte
les objets déplacés seulement s’ils viennent d’une boîte liste de fichiers.
procedure TFMForm.DirectoryOutline1DragOver(Sender, Source: TObject; X,
Y: Integer; State: TDragState; var Accept: Boolean);
begin
if Source is TFileListBox then
Accept := True;
else
Accept := False;
end;

6-2 Guide du développeur


Implémentation du glisser-déplacer dans les contrôles

Déplacement des éléments


Si un contrôle indique qu’il peut accepter un élément déplacé, il doit le traiter
s’il est effectivement lâché. Pour gérer les éléments lâchés, attachez un
gestionnaire à l’événement OnDragDrop du contrôle qui accepte l’opération
lâcher. Comme l’événement “glisser-dessus”, l’événement “glisser-déplacer”
indique la source de l’élément déplacé et les coordonnées du curseur de la souris
lorsqu’il est au-dessus du contrôle acceptant l’élément. Le dernier paramètre
vous permet de contrôler le chemin emprunté par un élément au cours de
l’opération glisser ; vous pouvez, par exemple, utiliser ces informations pour
modifier la couleur affichée par les composants au cours de leur transfert.
Dans l’exemple suivant, une arborescence de répertoires, qui accepte les éléments
déplacés depuis une boîte liste de fichiers, répond en déplaçant les fichiers vers
le répertoire sur lequel ils sont lâchés :
procedure TFMForm.DirectoryOutline1DragDrop(Sender, Source: TObject; X,
Y: Integer);
begin
if Source is TFileListBox then
with DirectoryOutline1 do
ConfirmChange('Move', FileList.FileName, Items[GetItem(X, Y)].FullPath);
end;

Fin de l’opération glisser-déplacer


Une opération glisser se termine lorsque l’élément est déplacé avec succès ou
qu’il est relâché au-dessus d’un contrôle qui ne peut pas l’acccepter. A ce stade
un événement “fin-glisser” est envoyé au contrôle à partir duquel l’élément a été
déplacé. Pour permettre à un contrôle de répondre quand des éléments en sont
extraits, attachez un gestionnaire à l’événement OnEndDrag du contrôle.
Le paramètre le plus important dans un événement OnEndDrag est appelé Target,
il indique quel contrôle, le cas échéant, accepte l’élément déplacé. Si Target est
nil, cela signifie qu’aucun contrôle ne l’accepte. L’événement OnEndDrag
comprend aussi les coordonnées du contrôle de réception.
Dans cet exemple, une boîte liste de fichiers gère un événement “fin-glisser” en
mettant à jour sa liste de fichiers.
procedure TFMForm.FileList1EndDrag(Sender, Target: TObject; X, Y: Integer);
begin
if Target <> nil then FileList1.Update;
end;

Manipulation des contrôles 6-3


Implémentation du glisser-empiler dans les contrôles

Personnalisation du glisser-déplacer avec un objet


déplacement
Vous pouvez utiliser un descendant de TDragObject pour personnaliser le
comportement glisser-déplacer d’un objet. Les événements “glisser-dessus” et
“glisser-déplacer” standard indiquent la source de l’élément glissé et les
coordonnées du curseur de souris au-dessus du contrôle qui l’accepte. Pour
obtenir des informations supplémentaires sur l’état en cours, dérivez un objet
glissé de TDragObject et surchargez ses méthodes virtuelles. L’objet glissé doit
être créé dans l’événement OnStartDrag.
Normalement, le paramètre source des événements “glisser-dessus” et “glisser-
déplacer” est le contrôle qui commence l’opération glisser. Si plusieurs sortes de
contrôles peuvent commencer une opération impliquant le même type de
données, la source doit gérer chaque sorte de contrôle. Lorsque vous utilisez un
descendant de TDragObject, toutefois, la source est l’objet glissé lui-même ; si
chaque contrôle crée le même type d’objet glissé dans son événement
OnStartDrag, la cible doit gérer uniquement une sorte d’objet. Les événements
“glisser-dessus” et “glisser-déplacer” peuvent indiquer si la source est un objet
glissé, en opposition au contrôle, en appelant la fonction IsDragObject.
Les objets glissés vous permettent de déplacer des éléments entre une fiche
implémentée dans le fichier EXE principal de l’application et une fiche implémentée
dans une DLL, ou entre des fiches implémentées dans différentes DLL.

Changement du pointeur de la souris


Il est possible de personnaliser l’aspect du pointeur de la souris lors d’opérations
glisser en définissant la propriété DragCursor du composant source.

Implémentation du glisser-empiler dans les contrôles


Les descendants de TWinControl peuvent faire office de sites empilés et les
descendants de TControl peuvent faire office de fenêtres enfant empilées dans les
sites d'empilement. Par exemple, pour fournir un site d'empilement sur le bord
gauche de la fenêtre d’une fiche, alignez un volet sur le bord gauche de la fiche
et faites-en un site d'empilement. Lorsque des contrôles empilables sont déplacés
vers le volet puis lâchés, ils deviennent des contrôles enfant du volet.
• Transformation d’un contrôle fenêtré en un site d’empilement
• Transformation d'un contrôle en un enfant empilable
• Contrôle de l'empilement des contrôles enfant
• Contrôle du désempilement des contrôles enfant
• Contrôle de la réponse des contrôles enfant aux opérations glisser-empiler

6-4 Guide du développeur


Implémentation du glisser-empiler dans les contrôles

Transformation d’un contrôle fenêtré en un site


d’empilement
Pour transformer un contrôle fenêtré en un site d'empilement :
1 Mettez la propriété DockSite à True.
2 Si l'objet site empilé ne doit apparaître que lorsqu'il contient un client empilé,
mettez sa propriété AutoSize à True. Lorsque AutoSize est à True, le site empilé
a pour taille 0 jusqu'à ce qu'il accepte d'empiler un contrôle enfant, après quoi
il est redimensionné de sorte qu'il englobe le contrôle enfant.

Transformation d'un contrôle en un enfant empilable


Pour transformer un contrôle en un enfant empilable :
1 Mettez sa propriété DragKind à dkDock. Lorsque DragKind est à dkDock, le fait
de faire glisser le contrôle déplace ce dernier vers un nouveau site
d'empilement ou désempile le contrôle qui devient une fenêtre flottante.
Lorsque DragKind est à dkDrag (valeur par défaut), le fait de faire glisser le
contrôle démarre une opération glisser-déplacer qui doit être implémentée à
l'aide des événements OnDragOver, OnEndDrag et OnDragDrop.
2 Mettez sa propriété DragMode à dmAutomatic. Lorsque DragMode est à
dmAutomatic, le glissement (glisser-déplacer ou empilement, suivant DragKind)
est automatiquement lancé lorsque l'utilisateur commence à faire glisser le
contrôle avec la souris. Lorsque DragMode est à dmManual, vous pouvez
commencer une opération glisser-empiler (ou glisser-déplacer) en appelant la
méthode BeginDrag.
3 Définissez sa propriété FloatingDockSiteClass pour indiquer le descendant
TWinControl qui doit héberger le contrôle lorsqu'il est désempilé et devient
une fenêtre flottante. Lorsque le contrôle est libéré et hors d'un site
d'empilement, un contrôle fenêtré de cette classe est dynamiquement créé et
devient le parent de l'enfant empilable. Si le contrôle enfant empilable est un
descendant de TWinControl, il n'est pas nécessaire de créer un site empilé
flottant séparé pour héberger le contrôle, bien qu'il soit possible de spécifier
une fiche pour obtenir une bordure et une barre de titre. Pour ignorer une
fenêtre conteneur dynamique, attribuez à FloatingDockSiteClass la même classe
que le contrôle et elle deviendra une fenêtre flottante sans parent.

Contrôle de l'empilement des contrôles enfant


Un site d'empilement accepte automatiquement les contrôles enfant lorsqu'ils
sont libérés au-dessus de lui. Pour la plupart des contrôles, le premier enfant est
empilé pour remplir la zone client, le deuxième divise cette dernière en
différentes régions, et ainsi de suite. Les contrôles de page empilent les enfants
dans de nouvelles feuilles à onglets (ou fusionnent dans des feuilles à onglets si
l'enfant est un autre contrôle de page).

Manipulation des contrôles 6-5


Implémentation du glisser-empiler dans les contrôles

Trois événements permettent aux sites d’influer sur l'empilement des contrôles
enfant :
property OnGetSiteInfo: TGetSiteInfoEvent;
TGetSiteInfoEvent = procedure (Sender: TObject; DockClient: TControl; var InfluenceRect:
TRect; var CanDock: Boolean) of object;
OnGetSiteInfo intervient sur le site d'empilement lorsque l'utilisateur fait glisser
un enfant empilable sur le contrôle. Il permet au site d'indiquer s'il accepte en
tant qu’enfant le contrôle spécifié par le paramètre DockClient et, si tel est le cas,
où l'enfant doit se trouver en vue de son empilement. Lorsque OnGetSiteInfo
intervient, InfluenceRect est initialisée selon les coordonnées d'écran du site
d'empilement et CanDock est intialisée à True. Une région d'empilement plus
limitée peut être créée en changeant InfluenceRect et l'enfant peut être rejeté en
mettant CanDock à False.
property OnDockOver: TDockOverEvent;
TDockOverEvent = procedure (Sender: TObject; Source: TDragDockObject; X, Y: Integer; State:
TDragState; var Accept: Boolean) of object;
OnDockOver intervient sur le site d'empilement lorsque l'utilisateur fait glisser un
enfant empilable sur le contrôle. Il est analogue à l'événement OnDragOver au
cours d'une opération normale de glisser-déposer. Utilisez-le pour indiquer que
l'enfant peut être relâché en vue de son empilement, en initialisant la propriété
Accept. Si le contrôle empilable est rejeté par le gestionnaire d'événement
OnGetSiteInfo (par exemple, si le type de contrôle est incorrect), OnDockOver ne
se produit pas.
property OnDockDrop: TDockDropEvent;
TDockDropEvent = procedure (Sender: TObject; Source: TDragDockObject; X, Y: Integer) of
object;
OnDockDrop intervient sur le site d'empilement lorsque l'utilisateur relâche
l'enfant empilable sur le contrôle. Il est analogue à l'événement OnDragDrop au
cours d'une opération normale de glisser-déposer. Utilisez-le pour faire en sorte
d'accepter le contrôle en tant que contrôle enfant. L'accès au contrôle enfant peut
être obtenu à l'aide de la propriété Control de TDockObject spécifié par le
paramètre Source.

Contrôle du désempilement des contrôles enfant


Un site d'empilement permet de désempiler automatiquement les contrôles
enfant lorsqu'ils sont déplacés et que leur propriété DragMode vaut dmAutomatic.
Les sites d'empilement peuvent réagir lorsque les contrôles enfant sont retirés, et
même empêcher le désempilement, dans un gestionnaire d'événement
OnUnDock :
property OnUnDock: TUnDockEvent;
TUnDockEvent = procedure (Sender: TObject; Client: TControl; var Allow: Boolean) of
object;

6-6 Guide du développeur


Manipulation du texte dans les contrôles

Le paramètre Client indique le contrôle enfant qui tente un désempilement et le


paramètre Allow permet au site d'empilement (Sender) de rejeter le
désempilement. Lorsque vous implémentez un gestionnaire d'événement
OnUnDock, il peut être utile de connaître les autres enfants éventuellement
empilés. Ces informations figurent dans la propriété en lecture seule DockClients,
qui est un tableau indexé de TControl. Le nombre de clients empilés est donné
par la propriété en lecture seule DockClientCount.

Contrôle de la réponse des contrôles enfant aux


opérations glisser-empiler
Les contrôles enfant empilables disposent de deux événements qui interviennent
au cours des opérations glisser-empiler. OnStartDock, analogue à l’événement
OnStartDrag d’une opération glisser-déposer, permet au contrôle enfant empilable
de créer un objet glisser personnalisé. OnEndDock, comme OnEndDrag, se produit
lorsque l’opération glisser s’achève.

Manipulation du texte dans les contrôles


Les sections suivantes expliquent comment utiliser les différentes fonctions des
contrôles éditeur de texte formaté et des contrôles mémo. Certaines de ces
fonctions peuvent aussi être utilisées avec les contrôles éditeur.
• Définition de l’alignement du texte
• Ajout de barres de défilement en mode exécution
• Ajout de l’objet Presse-papiers
• Sélection de texte
• Sélection de la totalité d’un texte
• Opérations couper, copier et coller
• Suppression du texte sélectionné
• Désactivation des éléments de menu
• Ajout d’un menu surgissant
• Gestion de l’événement OnPopup

Définition de l’alignement du texte


Dans un composant mémo ou éditeur de texte formaté, le texte peut être aligné
à gauche, à droite ou centré. Pour modifier l’alignement du texte, spécifiez la
propriété Alignment du composant. L’alignement n’est appliqué que si la
propriété WordWrap est à True ; si le retour à la ligne automatique est désactivé,
il n’existe pas de marge sur laquelle s’aligner.

Manipulation des contrôles 6-7


Manipulation du texte dans les contrôles

Par exemple, le code suivant attache un gestionnaire d'événement OnClick à


l'élément de menu Caractère|Gauche, puis attache le même gestionnaire
d'événement aux deux éléments de Droit et Centré du menu Caractère.
procedure TEditForm.AlignClick(Sender: TObject);
begin
Left1.Checked := False; { efface les trois coches }
Right1.Checked := False;
Center1.Checked := False;
with Sender as TMenuItem do Checked := True; { coche l’élément cliqué }
with Editor do { puis initialise Alignment pour faire correspondre }
if Left1.Checked then
Alignment := taLeftJustify
else if Right1.Checked then
Alignment := taRightJustify
else if Center1.Checked then
Alignment := taCenter;
end;

Ajout de barres de défilement en mode exécution


Les composants mémo ou éditeur de texte formaté peuvent contenir des barres
de défilement horizontales ou verticales ou les deux, selon les besoins. Lorsque
le retour à la ligne automatique est actif, le composant n’a besoin que d’une
barre de défilement vertical. Si l’utilisateur désactive le retour à la ligne
automatique, le composant a besoin aussi d’une barre de défilement horizontal,
puisque le texte n’est plus limité par le bord droit de l’éditeur.
Pour ajouter des barres de défilement en mode exécution :
1 Déterminez si le texte peut dépasser la marge droite. Dans la majorité des cas,
cela implique de tester si le retour à la ligne automatique est activé. Vous
devrez aussi vérifier qu’il existe réellement des lignes dépassant la largeur du
contrôle.
2 Définissez la propriété ScrollBars du composant mémo ou éditeur de texte
formaté de façon à inclure ou à exclure les barres de défilement.
L’exemple suivant attache le gestionnaire de l’événement OnClick à l’élément de
menu Caractères | Retour à la ligne.
procedure TEditForm.WordWrap1Click(Sender: TObject);
begin
with Editor do
begin
WordWrap := not WordWrap; { active ou désactive le retour à la ligne }
if WordWrap then
ScrollBars := ssVertical { seule la barre verticale est nécessaire }
else
ScrollBars := ssBoth; { deux barres peuvent être nécessaires }
WordWrap1.Checked := WordWrap; { coche ou désactive l’élément de menu }
end;
end;

6-8 Guide du développeur


Manipulation du texte dans les contrôles

Les composants mémo ou éditeur de texte formaté ne gèrent pas les barres de
défilement exactement de la même manière. Le composant éditeur de texte
formaté peut dissimuler ses barres de défilement si le texte ne sort pas des
limites du composant. Le composant Mémo affiche toujours les barres de
défilement lorsqu’elles ont été activées.

Ajout de l’objet Clipboard


La plupart des applications manipulant du texte permettent aux utilisateurs de
déplacer un texte sélectionné d’un document vers un autre, même s’il s’agit
d’une autre application. L’objet Clipboard de Delphi encapsule le Presse-papiers
de Windows et inclut les méthodes permettant de couper, de copier et de coller
du texte (ainsi que d’autres formats, par exemple les graphiques). L’objet
Clipboard est déclaré dans l’unité Clipbrd.
Pour ajouter l’objet Clipboard à une application,
1 Sélectionnez l’unité qui utilisera le Presse-papiers.
2 Recherchez le mot réservé implementation .
3 Ajoutez Clipbrd à la clause uses sous implementation .
• Si une clause uses existe déjà dans la partie implementation , ajoutez Clipbrd à
la fin de celle-ci.
• S’il n’y a pas de clause uses, ajoutez-en une rédigée ainsi :
uses Clipbrd;
Par exemple, dans une application contenant une fenêtre enfant, la clause uses
dans la partie implémentation de l’unité peut ressembler à ceci :
uses
MDIFrame, Clipbrd;

Sélection de texte
Pour transférer du texte dans le Presse-papiers, il faut d’abord sélectionner ce
texte. La possibilité de mettre en surbrillance le texte sélectionné est intégrée aux
composants éditeur. Lorsque l’utilisateur sélectionne un texte, celui-ci apparaît en
surbrillance.
Le tableau suivant dresse la liste des propriétés fréquemment utilisées pour la
manipulation du texte sélectionné.

Tableau 6.1 Propriétés du texte sélectionné


Propriété Description
SelText Contient une chaîne représentant le texte sélectionné dans le composant.
SelLength Contient la longueur d’une chaîne sélectionnée.
SelStart Contient la position de départ d’une chaîne.

Manipulation des contrôles 6-9


Manipulation du texte dans les contrôles

Sélection de la totalité d’un texte


La méthode SelectAll sélectionne la totalité du texte présent dans le composant
mémo ou éditeur de texte formaté. C’est particulièrement utile quand le contenu
de l’éditeur dépasse la zone visible du composant. Dans les autres cas, les
utilisateurs peuvent sélectionner du texte à l’aide du clavier ou de la souris.
Pour sélectionner la totalité du contenu d’un composant mémo ou éditeur de
texte formaté, appelez la méthode SelectAll du contrôle RichEdit1.
Par exemple,
procedure TMainForm.SelectAll(Sender: TObject);
begin
RichEdit1.SelectAll; { sélectionne tout le texte du composant RichEdit }
end;

Couper, copier et coller du texte


Grâce au Presse-papiers de Windows, les applications utilisant l’unité Clipbrd
peuvent couper, copier et coller du texte, des graphiques et des objets. Les
composants éditeur qui encapsulent les contrôles de manipulation de texte
standard de Windows disposent tous de méthodes intégrées autorisant les
interactions avec le Presse-papiers. Pour plus d’informations sur l’utilisation des
graphiques et du Presse-papiers, voir “Utilisation du Presse-papiers avec les
graphiques” à la page 7-22.
Pour couper, copier ou coller du texte avec le Presse-papiers, appelez
respectivement les méthodes CutToClipboard, CopyToClipboard et
PasteFromClipboard du composant.
Par exemple, le code suivant attache des gestionnaires aux événements OnClick
des commandes Edition | Couper, Edition | Copier et Edition | Coller :
procedure TEditForm.CutToClipboard(Sender: TObject);
begin
Editor.CutToClipboard;
end;
procedure TEditForm.CopyToClipboard(Sender: TObject);
begin
Editor.CopyToClipboard;
end;
procedure TEditForm.PasteFromClipboard(Sender: TObject);
begin
Editor.PasteFromClipboard;
end;

6-10 Guide du développeur


Manipulation du texte dans les contrôles

Effacement du texte sélectionné


Vous pouvez effacer le texte sélectionné dans un éditeur sans le placer dans le
Presse-papiers. Pour ce faire, appelez la méthode ClearSelection de l’éditeur. Par
exemple, s’il existe un élément Supprimer dans le menu Edition, votre code peut
ressembler à :
procedure TEditForm.Delete(Sender: TObject);
begin
RichEdit1.ClearSelection;
end;

Désactivation des éléments de menu


Il est souvent utile de désactiver des commandes de menus sans pour autant les
retirer du menu. Dans un éditeur de texte, par exemple, si aucun texte n’est
sélectionné, les commandes Couper, Copier et Supprimer du menu Edition sont
inapplicables. L’activation ou la désactivation des éléments de menu peut être
déclenchée lorsque l’utilisateur sélectionne le menu. Pour désactiver un élément
de menu, donnez la valeur False à sa propriété Enabled.
Dans l’exemple suivant, un gestionnaire est attaché à l’événement OnClick d’un
élément Edition appartenant à la barre de menu d’une fiche enfant. Il définit la
propriété Enabled des éléments Couper, Copier et Supprimer dans le menu
Edition, selon que du texte est sélectionné ou non dans le composant RichEdit1.
La commande Coller sera activée ou désactivée selon que le Presse-papiers
contient ou non du texte.
procedure TEditForm.Edit1Click(Sender: TObject);
var
HasSelection: Boolean; { déclare une variable temporaire }
begin
Paste1.Enabled := Clipboard.HasFormat(CF_TEXT); {active ou désactive l’élément Coller }
HasSelection := Editor.SelLength > 0; { True si du texte est sélectionné }
Cut1.Enabled := HasSelection; { activation des éléments si HasSelection vaut True }
Copy1.Enabled := HasSelection;
Delete1.Enabled := HasSelection;
end;
La méthode HasFormat du Presse-papiers renvoie une valeur booléenne indiquant
si le Presse-papiers contient des objets, du texte ou des images d’un format
particulier. En appelant HasFormat avec le paramètre CF_TEXT, vous pouvez
déterminer si le Presse-papiers contient du texte, et activer ou désactiver
l’élément Coller selon le cas.
Pour plus d’informations sur l’utilisation du Presse-Papiers avec des graphiques,
voir chapitre 7, “Utilisation des graphiques et du multimédia”.

Manipulation des contrôles 6-11


Manipulation du texte dans les contrôles

Ajout d’un menu surgissant


Les menus surgissants (ou locaux) sont d’un usage courant et faciles à mettre en
oeuvre dans toute sorte d’application. Ils réduisent le nombre d’opérations
nécessaires à la réalisation des tâches : en cliquant avec le bouton droit de la
souris sur l’espace de travail de l’application, l’utilisateur accède à une liste
regroupant les commandes les plus fréquemment utilisées.
Dans une application éditeur de texte, par exemple, vous pouvez ajouter un
menu surgissant qui comporte les commandes d’édition Couper, Copier et
Coller. Ces éléments de menu surgissant peuvent utiliser les mêmes gestionnaires
d’événements que les éléments correspondants du menu Edition. Il n’est pas
nécessaire de créer des raccourcis clavier, ni des touches raccourci pour les
menus surgissants, car les éléments des menus qui leur correspondent en
possèdent généralement.
La propriété PopupMenu d’une fiche indique quel menu surgissant doit s’afficher
lorsque l’utilisateur clique avec le bouton droit de la souris sur la fiche. Les
différents contrôles possèdent aussi leurs propriétés PopupMenu qui ont priorité
sur la propriété de la fiche, permettant de définir des menus personnalisés pour
des contrôles particuliers.
Pour ajouter un menu surgissant à une fiche,
1 Placez un composant menu surgissant sur la fiche.
2 Utilisez le concepteur de menus pour définir les éléments du menu surgissant.
3 Définissez par le nom du composant menu surgissant la propriété PopupMenu
de la fiche ou du contrôle devant faire apparaître le menu.
4 Attachez les gestionnaires aux événements OnClick des éléments du menu
surgissant.

Gestion de l’événement OnPopup


Il peut être nécessaire de préparer certains éléments d’un menu surgissant avant
d’afficher celui-ci, comme vous devez spécifier les éléments activés ou désactivés
d’un menu normal. Avec un menu normal, l’événement OnClick correspondant à
l’affichage du menu est généralement associé au titre de ce menu, comme décrit
dans la section “Désactivation des éléments de menu” à la page 6-11.
Comme les menus surgissants n’ont pas de barre de menu, vous devez gérer
l’événement dans le composant lui-même. Le composant menu surgissant offre
pour cela un événement particulier appelé OnPopup.
Pour préparer des éléments d’un menu surgissant avant de les afficher,
1 Sélectionnez le composant menu surgissant.
2 Attachez un gestionnaire à son événement OnPopup.
3 Ecrivez dans le gestionnaire d’événement le code activant, désactivant,
dissimulant ou affichant les éléments du menu.

6-12 Guide du développeur


Ajout de graphiques à des contrôles

Dans le code suivant, un gestionnaire existant pour l’événement EditEditClick


décrit précédemment dans la section “Désactivation des éléments de menu” à la
page 6-11 est attaché à l’événement OnPopup du composant menu surgissant.
Une ligne de code est ajoutée à EditEditClick pour chaque élément du menu
surgissant.
procedure TEditForm.Edit1Click(Sender: TObject);
var
HasSelection: Boolean;
begin
Paste1.Enabled := Clipboard.HasFormat(CF_TEXT);
Paste2.Enabled := Paste1.Enabled;{Ajoute cette ligne}
HasSelection := Editor.SelLength <> 0;
Cut1.Enabled := HasSelection;
Cut2.Enabled := HasSelection;{Ajoute cette ligne}
Copy1.Enabled := HasSelection;
Copy2.Enabled := HasSelection;{Ajoute cette ligne}
Delete1.Enabled := HasSelection;
end;

Ajout de graphiques à des contrôles


Les contrôles boîtes liste, boîtes à options et menu de Windows ont un style dit
“dessiné par le propriétaire” ; au lieu d’utiliser la méthode standard de Windows
dessinant le texte de chaque élément d’un contrôle, son propriétaire
(généralement la fiche) dessine ces éléments en mode exécution. L’utilisation la
plus courante de ces contrôles dessinés par le propriétaire est de remplacer le
texte par des dessins ou d’ajouter des dessins au texte des éléments. Pour des
informations sur l’utilisation du style “dessiné par le propriétaire” pour ajouter
des images aux menus, voir “Ajout d’images à des éléments de menu” à la
page 5-23.
Les contrôles dessinés par le propriétaire ont un point commun : ils contiennent
tous des listes d’éléments. Par défaut, il s’agit de listes de chaînes que Windows
affiche sous forme de texte. Il est possible d’associer un objet à chaque élément
de ces listes et d’utiliser l’objet lorsque vous dessinez un élément.
Dans Delphi, la création d’un contrôle dessiné par le propriétaire se fait
généralement en trois étapes :
1 Choix du style dessiné par le propriétaire
2 Ajout d’objets graphiques à une liste de chaînes
3 Dessiner des éléments dessinés par le propriétaire

Choix du style dessiné par le propriétaire


Les boîtes liste et les boîtes à options ont une propriété appelée Style. La propriété
Style détermine si le contrôle utilise le dessin par défaut (appelé style “standard”)
ou bien le dessin effectué par le propriétaire. Les grilles utilisent une propriété
appelée DefaultDrawing qui permet d’activer ou de désactiver le dessin par défaut.

Manipulation des contrôles 6-13


Ajout de graphiques à des contrôles

Pour les boîtes liste et les boîtes à options, il y a plusieurs styles dessinés par le
propriétaire, appelés fixed et variable, comme décrit dans le tableau suivant. Les
grilles dessinées par le propriétaire sont toujours “fixes” : bien que la taille des
lignes et des colonnes soit variable, la taille des cellules est fixée avant le dessin
de la grille.

Tableau 6.2 Comparaison entre les styles “fixed” et “variable”


Styles dessinés
par le propriétaire Signification Exemples
Fixed Chaque élément est de la même hauteur, lbOwnerDrawFixed,
déterminée par la propriété ItemHeight. csOwnerDrawFixed
Variable Chaque élément peut avoir une hauteur lbOwnerDrawVariable,
différente qui dépend des données au csOwnerDrawVariable
moment de l’exécution.

Ajout d’objets graphiques à une liste de chaînes


Toute liste de chaînes est capable de contenir une liste d’objets en plus de sa
liste de chaînes.
Dans une application de gestion de fichiers, par exemple, vous devez ajouter un
bitmap indiquant le type du lecteur à la lettre le désignant. Pour cela, vous
devez ajouter les images bitmap à l’application, puis les copier à l’endroit
approprié dans la liste de chaînes, comme le décrivent les sections suivantes.

Ajout d’images à une application


Un contrôle image est un contrôle non visuel qui contient une image graphique
(un bitmap, par exemple). Les contrôles image servent à afficher des images
graphiques sur une fiche, mais vous pouvez aussi les utiliser pour stocker des
images cachées que vous utiliserez dans votre application. Par exemple, il est
possible de stocker des images bitmap pour les contrôles dessinés par le
propriétaire dans des contrôles image cachés, comme décrit ci-dessous :
1 Ajoutez des contrôles image à la fiche principale.
2 Définissez leurs propriétés Name.
3 Donnez la valeur False à la propriété Visible de chaque contrôle image.
4 Définissez la propriété Picture de chaque contrôle image par le bitmap
souhaité en utilisant l’éditeur d’image depuis l’inspecteur d’objets.
Les contrôles image seront invisibles lorsque vous exécuterez l’application.

Ajout d’images à une liste de chaînes


Une fois que vous avez des images graphiques dans une application, vous
pouvez les associer aux chaînes de la liste. Vous pouvez soit ajouter les objets en
même temps que les chaînes, soit les associer à des chaînes qui ont déjà été
ajoutées. Si vous disposez de toutes les données dont vous avez besoin, vous
ajouterez sans doute les chaînes et les objets en même temps.

6-14 Guide du développeur


Ajout de graphiques à des contrôles

L’exemple suivant montre comment ajouter des images à une liste de chaînes. Ce
code est extrait d’une application de gestion de fichiers dans laquelle chaque
lecteur correct est représenté par une lettre et est associé à un bitmap indiquant
le type du lecteur. L’événement OnCreate se présente comme suit :
procedure TFMForm.FormCreate(Sender: TObject);
var
Drive: Char;
AddedIndex: Integer;
begin
for Drive := 'A' to 'Z' do { passe par tous les lecteurs possibles }
begin
case GetDriveType(Drive +':/') of { valeurs positives signifiant des lecteurs valides }
DRIVE_REMOVABLE: { ajoute un onglet }
AddedIndex := DriveTabSet.Tabs.AddObject(Drive, Floppy.Picture.Graphic);
DRIVE_FIXED: { ajoute un onglet }
AddedIndex := DriveTabSet.Tabs.AddObject(Drive, Fixed.Picture.Graphic);
DRIVE_REMOTE: { ajoute un onglet }
AddedIndex := DriveTabSet.Tabs.AddObject(Drive, Network.Picture.Graphic);
end;
if UpCase(Drive) = UpCase(DirectoryOutline.Drive) then { lecteur actif ? }
DriveTabSet.TabIndex := AddedIndex; { puis en fait l’onglet en cours }
end;
end;

Dessiner des éléments dessinés par le propriétaire


Lorsque vous avez défini le style d’un contrôle comme étant dessiné par le
propriétaire, Windows ne dessine plus le contrôle à l’écran. Au lieu de cela,
Windows génère un événement pour chaque élément visible du contrôle. C’est
votre application qui gère ces événements et dessine les éléments.
Pour dessiner les éléments d’un contrôle dessiné par le propriétaire, suivez les
étapes indiquées ci-après. Ces étapes se répètent pour chaque élément visible du
contrôle, mais vous utiliserez le même gestionnaire d’événement pour tous.
1 Le cas échéant, dimensionnez l’élément.
Si les éléments sont tous de même taille (par exemple, avec un style de boîte liste
lsOwnerDrawFixed), cette opération n’est pas nécessaire.
2 Dessinez l’élément.

Dimensionnement des éléments dessinés par le


propriétaire
Avant de laisser votre application dessiner chaque élément d’un contrôle de taille
variable lorsqu’il est dessiné par le propriétaire, Windows génère un événement
de type measure-item. Cet événement indique à l’application l’endroit où l’élément
apparaîtra sur le contrôle.

Manipulation des contrôles 6-15


Ajout de graphiques à des contrôles

Windows détermine la taille probable de l’élément (généralement juste assez


grand pour afficher le texte de l’élément dans la police de caractères active).
Votre application peut gérer l’événement et modifier la zone rectangle choisie
par Windows. Par exemple, si vous comptez remplacer le texte de l’élément par
une image bitmap, vous modifierez le rectangle pour qu’il soit de la taille du
bitmap. Si vous voulez avoir à la fois l’image et le texte, vous ajusterez la taille
du rectangle pour qu’il puisse contenir les deux.
Pour changer la taille d’un élément dessiné par le propriétaire, attachez un
gestionnaire à l’événement measure-item dans le contrôle dessiné par le
propriétaire. Le nom de l’événement peut varier en fonction du contrôle. Les
boîtes liste et les boîtes à options utilisent OnMeasureItem. Les grilles n’ont pas ce
type d’événement.
L’événement définissant la taille utilise deux paramètres importants : l’indice et
la taille de l’élément. Cette taille est variable : l’application peut l’augmenter ou
la diminuer. La position des éléments suivants dépend de la taille des éléments
précédents.
Par exemple, dans une boîte liste variable dessinée par le propriétaire, si
l’application définit la hauteur du premier élément à cinq pixels, le second
élément commence au sixième pixel depuis le haut, et ainsi de suite. Dans les
boîtes liste et dans les boîtes à options, le seul aspect des éléments que
l’application puisse changer est la hauteur. La largeur de l’élément est toujours
celle du contrôle.
Les grilles dessinées par le propriétaire ne peuvent pas modifier la taille des
cellules au fur et à mesure qu’elles sont dessinées. En effet, la taille des lignes et
des colonnes est définie avant le dessin par les propriétés ColWidths et
RowHeights.
Le code suivant, attaché à l’événement OnMeasureItem du composant boîte liste
dessinée par le propriétaire, augmente la hauteur de chaque élément de liste
pour permettre de placer l’image bitmap associée.
procedure TFMForm.DriveTabSetMeasureTab(Sender: TObject; Index: Integer;
var TabWidth: Integer); { notez que TabWidth définit un paramètre var}
var
BitmapWidth: Integer;
begin
BitmapWidth := TBitmap(DriveTabSet.Tabs.Objects[Index]).Width;
{ augmente la largeur de l’onglet de celle du bitmap associé plus deux }
Inc(TabWidth, 2 + BitmapWidth);
end;
Remarque Vous devez transtyper les éléments à partir de la propriété Objects dans la liste
de chaînes. Objects est une propriété de type TObject, aussi peut-elle contenir
n’importe quel type d’objet. Lorsque vous extrayez un objet d’un tableau, vous
devez le transtyper afin qu’il reprenne le type des éléments.

6-16 Guide du développeur


Ajout de graphiques à des contrôles

Dessin de chaque élément dessiné par le propriétaire


Lorsqu’une application doit dessiner ou redessiner un contrôle dessiné par le
propriétaire, Windows génère un événement de type draw-item pour chaque
élément visible du contrôle.
Pour dessiner chaque élément d’un contrôle dessiné par le propriétaire, attachez
un gestionnaire à l’événement draw-item de ce contrôle.
Le nom des événements relatifs aux objets dessinés par le propriétaire commence
par OnDraw, comme OnDrawItem ou OnDrawCell.
L’événement draw-item contient des paramètres indiquant l’indice de l’élément à
dessiner, le rectangle dans lequel il s’inscrit et, habituellement, des informations
sur son état (actif, par exemple). L’application gère chaque événement en plaçant
l’élément approprié dans le rectangle transmis.
Par exemple, le code suivant montre comment dessiner des éléments dans une
boîte liste ayant un bitmap associé à chaque chaîne. Il attache ce gestionnaire à
l’événement OnDrawItem :
procedure TFMForm.DriveTabSetDrawTab(Sender: TObject; TabCanvas: TCanvas;
R: TRect; Index: Integer; Selected: Boolean);
var
Bitmap: TBitmap;
begin
Bitmap := TBitmap(DriveTabSet.Tabs.Objects[Index]);
with TabCanvas do
begin
Draw(R.Left, R.Top + 4, Bitmap); { dessine le bitmap }
TextOut(R.Left + 2 + Bitmap.Width, { positionne le texte }
R.Top + 2, DriveTabSet.Tabs[Index]); { et le dessine à droite du bitmap }
end;
end;

Manipulation des contrôles 6-17


6-18 Guide du développeur
Chapitre

Utilisation des graphiques


Chapter 7
7
et du multimédia
Les éléments graphiques et multimédia permettent d’améliorer la présentation de
vos applications. Delphi propose divers moyens d’introduire ces caractéristiques
dans votre application. Pour ajouter des éléments graphiques, vous pouvez
insérer des images pré-dessinées à la conception, les créer en utilisant des
contrôles graphiques à la conception ou les dessiner dynamiquement à
l’exécution. Pour ajouter des fonctions multimédia, Delphi propose des
composants spéciaux qui peuvent jouer des séquences audio et vidéo.

Présentation de la programmation relative aux


graphiques
La VCL des composants graphiques encapsule la GDI (Graphics Device Interface)
de Windows. Il est ainsi très simple d’ajouter des graphiques à vos programmes
Windows.
Lorsque vous dessinez des graphiques dans une application Delphi, vous travaillez
sur le canevas, de l’objet, plutôt que directement sur l’objet. Le mot Canvas désigne
une propriété de l’objet, mais c’est aussi un objet. Le principal avantage de l’objet
canevas est qu’il gère efficacement des ressources et prend en compte le contexte
de périphérique. Que vous dessiniez sur des bitmaps, sur l’écran, sur
l’imprimante ou sur des métafichiers, vos programmes peuvent utiliser les
mêmes méthodes. Les canevas sont uniquement disponibles en phase d’exécution.
Tout le travail relatif aux canevas se fait donc en écrivant du code. Les sections
suivantes expliquent comment utiliser les composants graphiques de la VCL
pour simplifier les opérations de programmation.
Remarque Puisque TCanvas est un gestionnaire de ressources qui enveloppe le contexte de
périphérique Windows, vous pouvez aussi utiliser toutes les fonctions GDI de

Utilisation des graphiques et du multimédia 7-1


Présentation de la programmation relative aux graphiques

Windows sur le canevas. La propriété Handle du canevas est le handle du


contexte de périphérique.
La façon dont les images graphiques apparaissent dans votre application dépend
de la façon dont elles sont dessinées. Si vous dessinez directement dans le
canevas d’un contrôle, l’image est affichée directement. Toutefois, si vous
dessinez sur une image hors écran comme un canevas Tbitmap, l’image
n’apparaît que lorsqu’un contrôle effectue la copie d’un bitmap sur le canevas du
contrôle. Ce qui signifie que lorsqu’on dessine des bitmaps et qu’on les affecte à
un contrôle image, l’image n’apparaît que si le contrôle a la possibilité de traiter
son message OnPaint.
Lorsqu’on travaille avec des graphiques, on rencontre fréquemment les termes
dessin et peinture :
• Lorsque vous dessinez, vous créez avec du code un seul élément graphique
spécifique tel qu’une ligne ou une forme. Dans votre code, vous indiquez à un
objet de dessiner un graphique particulier à un emplacement particulier sur son
canevas en appelant une méthode de dessin du canevas.
• Lorsque vous peignez, vous créez l’apparence entière d’un objet. La peinture
implique généralement le dessin. En effet, en réponse à des événements OnPaint,
un objet dessinera des graphiques. Une boîte de saisie, par exemple, se peint elle-
même en dessinant un rectangle, puis en dessinant du texte à l’intérieur. Un
contrôle forme, en revanche, se peint lui-même en dessinant un seul graphique.
Les exemples donnés au début de ce chapitre démontrent comment dessiner divers
graphiques en réponse à des événements OnPaint. Les sections ultérieures
montrent comment faire le même type de dessin en réponse à d’autres événements.

Rafraîchissement de l’écran
A certains moments, Windows détermine que l’apparence de certains objets
affichés à l’écran doit être rafraîchie. Il génère donc des messages WM_PAINT,
que la VCL redirige vers des gestionnaires d’événements OnPaint. Lorsque vous
utilisez la méthode Refresh, la VCL appelle n’importe quel gestionnaire
d’événement OnPaint ayant été écrit pour cet objet. Par défaut, Delphi nomme ce
gestionnaire d’événement FormPaint. La méthode Refresh est parfois utilisée pour
rafraîchir un composant sur une fiche. Par exemple, la méthode Refresh peut être
appelée dans le gestionnaire d’événement OnResize de la fiche afin de réafficher
des graphiques ou pour dessiner un fond sur la fiche.
Bien que certains systèmes d’exploitation gèrent automatiquement l’affichage des
zones clientes d’une fenêtre qui ne sont plus valides, Windows ne le fait pas.
Pour Windows, tout dessin est considéré comme permanent. Lorsqu’une fiche ou
un contrôle est temporairement masqué, par exemple lors d’un glisser-déplacer,
la fiche ou le contrôle doivent repeindre la zone masquée lorsqu’elle ne l’est
plus. Pour plus d’informations sur le message WM_PAINT, voir l’aide en ligne
de Windows.
Lors de l’utilisation du contrôle TImage, la VCL gère automatiquement le dessin
et le rafraîchissement du graphique contenu dans le TImage. Dessiner sur un

7-2 Guide du développeur


Présentation de la programmation relative aux graphiques

TImage crée une image persistante. Par conséquent, il n’est pas nécessaire de
redessiner l’image contenue. Au contraire, le canevas d’un TPaintBox écrit
directement sur le pilote de l’écran, et de ce fait, tout ce qui est dessiné sur le
canevas du PaintBox est transitoire. Cela est vrai pour les contrôles similaires, y
compris la fiche elle-même. De plus, si vous dessinez ou peignez à partir du
constructeur d’un TPaintBox, vous devrez ajouter ce code dans le gestionnaire
OnPaint afin que l’image soit repeinte à chaque fois que la zone cliente est
invalidée.

Types des objets graphiques


La VCL propose les objets graphiques énumérés dans le tableau 7.1. Ces objets
disposent de méthodes pour dessiner dans le canevas décrites dans la section
“Utilisation des méthodes du canevas pour dessiner des objets graphiques” à la
page 7-10 et pour charger et enregistrer des fichiers graphiques (voir
“Chargement et enregistrement de fichiers graphiques” à la page 7-20).

Tableau 7.1 Types d’objets graphiques


Objet Description
Picture Utilisé pour contenir une image graphique. Pour ajouter d’autres formats de
fichiers graphiques, utilisez la méthode Register de l’objet Picture. Elle permet
de gérer des fichiers arbitraires comme l’affichage d’images dans un contrôle
image.
Bitmap Objet graphique utilisé pour créer des images, les manipuler (mise à l’échelle,
défilement, rotation et peinture) et les stocker sur disque sous forme de
fichiers. Il est très facile de créer la copie d’un bitmap, puisque c’est le handle
qui est copié et non l’image.
Clipboard Représente le conteneur d’un texte ou d’un graphique qui est coupé, copié ou
collé depuis ou vers une application. Grâce au Presse-papiers, vous pouvez
extraire des données en fonction d’un format donné ; comptage des
références d’handles, et l’ouverture et la fermeture du Presse-papiers ; gérer
et manipuler des formats pour les objets du Presse-papiers.
Icon Représente la valeur chargée depuis un fichier icône Windows (fichier .ICO).
Metafile Contient un métafichier, qui enregistre les opérations nécessaires à la
construction d’une image, au lieu de contenir les pixels du bitmap de
l’image. Les métafichiers sont extrêmement réductibles sans perte de détail de
l’image et nécessitent souvent moins de mémoire que les bitmaps,
particulièrement pour les pilotes haute résolution comme les imprimantes.
Mais les métafichiers ne sont pas aussi rapides que les bitmaps. Utilisez les
métafichiers lorsque vous recherchez de la souplesse au détriment des
performances.

Utilisation des graphiques et du multimédia 7-3


Présentation de la programmation relative aux graphiques

Propriétés et méthodes communes du canevas


Le tableau suivant énumère les principales propriétés de l’objet canevas. Pour
une liste complète des propriétés et des méthodes, voir la rubrique traitant du
composant TCanvas dans l’aide en ligne.

Tableau 7.2 Propriétés communes de l’objet canevas


Propriété Description
Font Spécifie la police devant être utilisée pour écrire du texte sur l’image.
Définit les propriétés de l’objet TFont afin de spécifier le type de police, sa
couleur, sa taille, et son style.
Brush Détermine la couleur et le modèle utilisés par le canevas pour remplir les
fonds et les formes graphiques. Définissez les propriétés de l’objet TBrush
pour spécifier la couleur et le modèle ou le bitmap à utiliser lors du
remplissage des espaces sur le canevas.
Pen Spécifie le type de crayon utilisé par le canevas pour dessiner des lignes et
des formes. Définissez les propriétés de l’objet TPen de façon à spécifier la
couleur, le style, la largeur et le mode du crayon.
PenPos Spécifie la position de dessin en cours du crayon.
Pixels Spécifie la couleur des pixels à l’intérieur du ClipRect en cours.

Pour davantage d’informations sur ces propriétés, voir “Utilisation des propriétés
de l’objet canevas” à la page 7-5.
Le tableau suivant liste les différentes méthodes pouvant être utilisées :

Tableau 7.3 Méthodes communes de l’objet canevas


Méthode Description
Arc Dessine un arc sur l’image ainsi que le périmètre de l’ellipse délimitée par
le rectangle spécifié.
Chord Dessine une figure fermée représentée par l’intersection d’une ligne et
d’une ellipse.
CopyRect Copie une partie de l’image d’un autre canevas sur le canevas.
Draw Dessine sur le canevas à l’emplacement donné par les coordonnées (X, Y)
l’objet graphique spécifié par le paramètre Graphic.
Ellipse Dessine sur le canevas l’ellipse définie par un rectangle délimité.
FillRect Remplit sur le canevas le rectangle spécifié en utilisant le pinceau en
cours.
FloodFill Remplit une zone du canevas en utilisant le pinceau en cours.
FrameRect Dessine un rectangle en utilisant le pinceau du canevas pour dessiner sa
bordure.
LineTo Dessine une ligne sur le canevas en partant de la position de PenPos au
point spécifié par X et Y, et définit la position du crayon à (X, Y).
MoveTo Change la position de dessin en cours par le point (X,Y).
Pie Dessine sur le canevas un secteur de l’ellipse délimitée par le rectangle
(X1, Y1) et (X2, Y2).

7-4 Guide du développeur


Présentation de la programmation relative aux graphiques

Tableau 7.3 Méthodes communes de l’objet canevas (suite)


Méthode Description
Polygon Dessine sur le canevas une série de lignes connectant les points transmis
et fermant la forme par une ligne allant du dernier point au premier
point.
PolyLine Dessine sur le canevas une série de lignes à la position en cours du
crayon et connectant chacun des points transmis dans Points.
Rectangle Dessine sur le canevas un rectangle dont le coin supérieur gauche apparaît
au point (X1, Y1) et le coin inférieur droit au point (X2, Y2). Utilisez
Rectangle pour dessiner un cadre utilisant Pen et remplissez-le avec Brush.
RoundRect Dessine sur le canevas un rectangle à coins arrondis.
StretchDraw Dessine sur le canevas un graphique afin que l’image tienne dans le
rectangle spécifié. Le facteur d’amplification de l’image devra sans doute
être modifié pour que l’image tienne dans le rectangle.
TextHeight, Renvoie respectivement la hauteur et la largeur d’une chaîne dans la
TextWidth police en cours. La hauteur inclut l’intervalle entre les lignes.
TextOut Ecrit sur le canevas une chaîne commençant au point (X,Y), puis modifie
PenPos par rapport à la fin de la chaîne.
TextRect Ecrit une chaîne à l’intérieur d’une région ; toute partie de la chaîne se
trouvant à l’extérieur de la région ne sera pas visible.

Pour davantage d’informations sur ces méthodes, voir “Utilisation des méthodes
du canevas pour dessiner des objets graphiques” à la page 7-10.

Utilisation des propriétés de l’objet canevas


A l’objet canevas, il est possible de définir les propriétés d’un crayon afin qu’il
dessine des lignes, celles d’un pinceau pour qu’il remplisse des formes, celles
d’une fonte pour écrire du texte et celles d’un tableau de pixels pour représenter
une image.
Cette section traite des sujets suivants :
• Utilisation des crayons
• Utilisation des pinceaux
• Lecture et définition des pixels

Utilisation des crayons


La propriété Pen d’un canevas contrôle la façon dont les lignes apparaissent, y
compris les lignes dessinées pour définir le pourtour d’une forme. Dessiner une
ligne droite revient à changer un groupe de pixels alignés entre deux points.
Le crayon lui-même possède quatre propriétés qu’il est possible de changer : Color,
Width, Style, et Mode.
• Propriété Color: modifie la couleur du crayon
• Propriété Width: modifie la largeur du crayon
• Propriété Style: modifie le style du crayon
• Propriété Mode: modifie le mode du crayon

Utilisation des graphiques et du multimédia 7-5


Présentation de la programmation relative aux graphiques

Les valeurs de ces propriétés déterminent la façon dont le crayon change les pixels
de la ligne. Par défaut, chaque crayon est noir, a une largeur de 1 pixel, est de style
uni, et a un mode appelé copie qui écrase tout ce qui se trouve déjà sur le canevas.

Changement de la couleur du crayon


La couleur du crayon est définie en mode exécution comme toute autre propriété
Color. La couleur du crayon détermine la couleur des lignes qu’il dessine : lignes,
polylignes et contour des formes, ainsi que d’autres types de lignes et polylignes.
Pour modifier la couleur du crayon, donnez une valeur à la propriété Color du
crayon.
Pour permettre à l’utilisateur de choisir une nouvelle couleur de crayon, vous
devez placer une grille de couleurs dans la barre d’outils du crayon. Une grille
de couleurs permet de spécifier une couleur de premier plan et une couleur
d’arrière-plan. Si vous n’utilisez pas de grille, vous devez penser à fournir une
couleur d’arrière-plan pour dessiner les intervalles entre les segments de lignes.
La couleur d’arrière-plan provient de la propriété Color du pinceau.
Quand l’utilisateur choisit une nouvelle couleur en cliquant dans la grille, ce code
modifie la couleur du crayon en réponse à l’événement OnClick :
procedure TForm1.PenColorClick(Sender: TObject);
begin
Canvas.Pen.Color := PenColor.ForegroundColor;
end;

Changement de l’épaisseur du crayon


L’épaisseur du crayon détermine la taille, exprimée en pixels, de la ligne qu’il dessine.
Remarque N’oubliez pas que lorsque l’épaisseur est supérieure à un pixel, Windows 95
dessine toujours une ligne continue, sans tenir compte de la valeur de la propriété
Style du crayon.
Pour modifier l’épaisseur du crayon, affectez une valeur numérique à la propriété
Width du crayon.
Supposons que la barre d’outils du crayon contienne une barre de défilement
permettant de définir la largeur de celui-ci, et que vous vouliez mettre à jour le
libellé attenant à la barre de défilement pour que l’utilisateur voit ce qu’il fait.
Pour utiliser la position de la barre de défilement afin de déterminer l’épaisseur du
crayon, il est nécessaire de changer l’épaisseur du crayon chaque fois que la
position change.
Voici comment traiter l’événement OnChange de la barre de défilement :
procedure TForm1.PenWidthChange(Sender: TObject);
begin
Canvas.Pen.Width := PenWidth.Position;{ définit la largeur de crayon directement }
PenSize.Caption := IntToStr(PenWidth.Position);{ conversion en chaîne pour le libellé }
end;

7-6 Guide du développeur


Présentation de la programmation relative aux graphiques

Changement du style du crayon


La propriété Style d’un crayon permet de créer des lignes continues, pointillées ou à
tirets.
Remarque Si un crayon a une largeur supérieure à un pixel, Windows 95 ne permet pas de
dessiner des lignes pointillées ou à tirets. Il dessine à la place une ligne continue,
quel que soit le style spécifié.
La définition des propriétés d’un crayon est une opération qui se prête
parfaitement au partage d’événements entre plusieurs contrôles. Pour déterminer
le contrôle qui reçoit l’événement, il suffit de tester le paramètre Sender.
Pour créer un gestionnaire pour l’événement clic de chacun des six boutons de
style de la barre d’outils d’un crayon, procédez comme suit :
1 Sélectionnez les six boutons de style de crayon et choisissez Inspecteur d’objets|
Evénements|événement OnClick et tapez SetPenStyle dans la colonne des
gestionnaires.
Delphi génère un gestionnaire vide appelé SetPenStyle et l’attache à l’événement
OnClick de chacun des six boutons.
2 Remplissez le gestionnaire de l’événement Onclick en définissant le style du
crayon selon la valeur du paramètre Sender qui désigne le contrôle ayant envoyé
l’événement clic :
procedure TForm1.SetPenStyle(Sender: TObject);
begin
with Canvas.Pen do
begin
if Sender = SolidPen then Style := psSolid
else if Sender = DashPen then Style := psDash
else if Sender = DotPen then Style := psDot
else if Sender = DashDotPen then Style := psDashDot
else if Sender = DashDotDotPen then Style := psDashDotDot
else if Sender = ClearPen then Style := psClear;
end;
end;

Changement du mode du crayon


La propriété Mode d’un crayon vous permet de spécifier les différentes façons de
combiner la couleur du crayon à celle du canevas. Par exemple, le crayon peut
toujours être noir, être de la couleur inverse à l’arrière-plan du canevas, etc. Pour
plus de détails, voir la rubrique traitant de TPen dans l’aide en ligne.

Renvoi de la position du crayon


La position de dessin en cours, position à partir de laquelle le crayon va dessiner
la prochaine ligne, est appelée la position du crayon. Le canevas stocke la
position du crayon dans sa propriété PenPos. Cette position n’affecte que le
dessin des lignes ; pour les formes et le texte, vous devez spécifier toutes les
coordonnées dont vous avez besoin.

Utilisation des graphiques et du multimédia 7-7


Présentation de la programmation relative aux graphiques

Pour définir la position du crayon, appelez la méthode MoveTo du canevas. Par


exemple, le code suivant déplace la position du crayon sur le coin supérieur
gauche du canevas :
Canvas.MoveTo(0, 0);
Remarque Lorsqu’une ligne est dessinée avec la méthode LineTo, la position en cours est
déplacée sur le point d’arrivée de la ligne.

Utilisation des pinceaux


La propriété Brush d’un canevas détermine la façon dont les zones sont remplies, y
compris l’intérieur des formes. Remplir une zone avec le pinceau revient à changer
d’une certaine façon un grand nombre de pixels adjacents.
Le pinceau a trois propriétés que vous pouvez manipuler :
• Propriété Color : modifie la couleur de remplissage
• Propriété Style : modifie le style du pinceau
• Propriété Bitmap : utilise un bitmap comme modèle de pinceau
Les valeurs de ces propriétés déterminent la façon dont le canevas remplit les
formes et d’autres zones. Par défaut, chaque pinceau est blanc, a un style uni et n’a
pas de motif de remplissage.

Changement de la couleur du pinceau


La couleur du pinceau détermine la couleur utilisée par le canevas pour remplir les
formes. Pour modifier la couleur de remplissage, affectez une valeur à la propriété
Color du pinceau. Le pinceau est utilisé pour la couleur d’arrière-plan dans le
dessin de lignes et de texte.
Il est possible de définir la couleur du pinceau de la même manière que celle du
crayon, en réponse à un clic dans la grille de couleurs présentée dans la barre d’outils
du pinceau (voir la section “Changement de la couleur du crayon” à la page 7-6) :
procedure TForm1.BrushColorClick(Sender: TObject);
begin
Canvas.Brush.Color := BrushColor.ForegroundColor;
end;

Changement du style du pinceau


Le style d’un pinceau détermine le motif utilisé pour remplir les formes. Il vous
permet de spécifier différentes façons de combiner la couleur du pinceau à des
couleurs déjà présentes sur le canevas. Les styles prédéfinis comprennent des
couleurs unies, pas de couleur et divers motifs de lignes et de hachurages.
Pour modifier le style d’un pinceau, définissez sa propriété Style par l’une des
valeurs prédéfinies suivantes : bsSolid, bsClear, bsHorizontal, bsVertical, bsFDiagonal,
bsBDiagonal, bsCross ou bsDiagCross.
Cet exemple définit le style du pinceau en faisant partager le même gestionnaire
d’événement OnClick aux huit boutons de style de pinceau. Tous les boutons sont
sélectionnés, OnClick est sélectionné dans Inspecteur d’objets|Evénements et le
gestionnaire OnClick porte le nom SetBrushStyle.

7-8 Guide du développeur


Présentation de la programmation relative aux graphiques

procedure TForm1.SetBrushStyle(Sender: TObject);


begin
with Canvas.Brush do
begin
if Sender = SolidBrush then Style := bsSolid
else if Sender = ClearBrush then Style := bsClear
else if Sender = HorizontalBrush then Style := bsHorizontal
else if Sender = VerticalBrush then Style := bsVertical
else if Sender = FDiagonalBrush then Style := bsFDiagonal
else if Sender = BDiagonalBrush then Style := bsBDiagonal
else if Sender = CrossBrush then Style := bsCross
else if Sender = DiagCrossBrush then Style := bsDiagCross;
end;
end;

Définition de la propriété Bitmap du pinceau


La propriété Bitmap du pinceau vous permet de spécifier une image bitmap qui
sera utilisée comme motif de remplissage des formes et des autres zones.
L’exemple suivant charge un bitmap d’un fichier et l’affecte au pinceau du
canevas de la fiche Form1 :
var
Bitmap: TBitmap;
begin
Bitmap := TBitmap.Create;
try
Bitmap.LoadFromFile('MyBitmap.bmp');
Form1.Canvas.Brush.Bitmap := Bitmap;
Form1.Canvas.FillRect(Rect(0,0,100,100));
finally
Form1.Canvas.Brush.Bitmap := nil;
Bitmap.Free;
end;
end;
Remarque Le pinceau n’assume pas la possession d’un objet bitmap affecté à sa propriété
Bitmap. Vous devez vous assurer que l’objet Bitmap reste valide pendant la
durée de vie du pinceau, après quoi vous devez vous-même libérer l’objet
Bitmap.

Lecture et définition de pixels


Chaque canevas a une propriété Pixels indexée qui représente les points de couleur
constituant l’image sur le canevas. Vous devrez rarement accéder directement à la
propriété Pixels, sauf si vous voulez connaître ou modifier la couleur d’un pixel
particulier.
Remarque La définition de pixels individuels prend beaucoup plus de temps que les
opérations graphiques sur des zones particulières. N’utilisez pas la propriété
tableau Pixel pour accéder aux pixels d’une image dans un tableau général. Pour
un accès performant aux pixels d’une image, voir la propriété TBitmap.ScanLine.

Utilisation des graphiques et du multimédia 7-9


Présentation de la programmation relative aux graphiques

Utilisation des méthodes du canevas pour dessiner


des objets graphiques
Cette section montre comment utiliser certaines méthodes pour dessiner des
objets graphiques. Elle traite des sujets suivants :
• Dessin de lignes et de polylignes
• Dessin de formes
• Dessin de rectangles arrondis
• Dessin de polygones

Dessin de lignes et de polylignes


Un canevas peut dessiner des lignes droites et des polylignes (ou lignes brisées).
Une ligne droite est une ligne de pixels reliant deux points. Une polyligne est une
chaîne de lignes droites, reliées bout à bout. Le canevas dessine toutes les lignes en
utilisant son crayon.

Dessin de lignes
Pour dessiner une ligne droite sur un canevas, utilisez la méthode LineTo du
canevas.
La méthode LineTo dessine une ligne partant de la position en cours du crayon et
allant au point spécifié, et fait du point d’arrivée de la ligne la position en cours. Le
canevas dessine la ligne en utilisant son crayon.
Par exemple, la méthode suivante dessine des lignes diagonales qui se croisent sur
une fiche, chaque fois que la fiche est peinte :
procedure TForm1.FormPaint(Sender: TObject);
begin
with Canvas do
begin
MoveTo(0, 0);
LineTo(ClientWidth, ClientHeight);
MoveTo(0, ClientHeight);
LineTo(ClientWidth, 0);
end;
end;

Dessin de polylignes
En plus des lignes individuelles, le canevas peut dessiner des polylignes, qui
sont des groupes composés d’un nombre quelconque de segments de ligne reliés
entre eux.
Pour dessiner une polyligne sur un canevas, appelez la méthode PolyLine du canevas.
Le paramètre passé à la méthode PolyLine est un tableau de points. Imaginez
qu’une polyligne réalise une méthode MoveTo sur le premier point et une méthode
LineTo sur chaque point successif. Si vous voulez dessiner plusieurs lignes, vous
devez savoir que Polyline est plus rapide que la méthode MoveTo et que la
méthode LineTo, car elle élimine un certain nombre d’appels supplémentaires.

7-10 Guide du développeur


Présentation de la programmation relative aux graphiques

La méthode suivante, par exemple, dessine un losange dans une fiche :


procedure TForm1.FormPaint(Sender: TObject);
begin
with Canvas do
PolyLine([Point(0, 0), Point(50, 0), Point(75, 50), Point(25, 50), Point(0, 0)]);
end;s
Cet exemple montre bien les possibilités de Delphi de créer un paramètre tableau
ouvert à la volée. Il est possible de passer n’importe quel tableau de points, mais
une manière simple de construire un tableau facilement consiste à mettre ses
éléments entre crochets et de passer le tout en paramètre. Pour plus
d’informations, voir l’aide en ligne.

Dessin de formes
Les canevas disposent de méthodes vous permettant de dessiner différents types
de formes. Le canevas dessine le pourtour d’une forme avec son crayon, puis
remplit l’intérieur avec son pinceau. La ligne qui définit la bordure de la forme
est déterminée par l’objet Pen en cours.
Cette section couvre :
• Dessin de rectangle et d’ellipses
• Dessin de rectangles à coins arrondis
• Dessin de polygones

Dessin de rectangles et d’ellipses


Pour dessiner un rectangle ou une ellipse sur un canevas, appelez la méthode
Rectangle ou la méthode Ellipse du canevas, en transmettant les coordonnées des
limites d’un rectangle.
La méthode Rectangle dessine le rectangle ; Ellipse dessine une ellipse qui touche
tous les côtés du rectangle.
La méthode suivante dessine un rectangle remplissant le quart supérieur gauche
d’une fiche, puis dessine une ellipse sur la même zone :
procedure TForm1.FormPaint(Sender: TObject);
begin
Canvas.Rectangle(0, 0, ClientWidth div 2, ClientHeight div 2);
Canvas.Ellipse(0, 0, ClientWidth div 2, ClientHeight div 2);
end;

Dessin de rectangles à coins arrondis


Pour dessiner un rectangle à coins arrondis sur un canevas, appelez la méthode
RoundRect du canevas.
Les quatre premiers paramètres transmis à RoundRect sont les limites d’un
rectangle, comme pour la méthode Rectangle ou la méthode Ellipse. RoundRect
prend deux paramètres supplémentaires qui indiquent comment dessiner les coins
arrondis.

Utilisation des graphiques et du multimédia 7-11


Présentation de la programmation relative aux graphiques

La méthode suivante, par exemple, dessine un rectangle à coins arrondis dans le


quart supérieur de la fiche, en arrondissant les coins en arcs d’un cercle de
10 pixels de diamètre :
procedure TForm1.FormPaint(Sender: TObject);
begin
Canvas.RoundRect(0, 0, ClientWidth div 2, ClientHeight div 2, 10, 10);
end;

Dessin de polygones
Pour dessiner, sur un canevas, un polygone ayant un nombre quelconque de côtés,
appelez la méthode Polygon du canevas.
Polygon prend un tableau de points comme seul paramètre et relie les points avec le
crayon, puis relie le dernier point au premier de façon à fermer le polygone. Après
avoir dessiné les lignes, Polygon utilise le pinceau pour remplir la zone interne au
polygone.
Le code suivant dessine un triangle rectangle dans la moitié inférieure gauche de la
fiche :
procedure TForm1.FormPaint(Sender: TObject);
begin
Canvas.Polygon([Point(0, 0), Point(0, ClientHeight),
Point(ClientWidth, ClientHeight)]);
end;

Gestion de plusieurs objets de dessin dans votre


application
Différentes méthodes de dessin (rectangle, forme, ligne, etc.) sont typiquement
disponibles sur la barre d’outils et le volet de boutons. Les applications peuvent
répondre à des clics sur des turboboutons de façon à définir les objets de dessin
voulu. Cette section décrit comment :
• Faire le suivi de l’outil de dessin à utiliser
• Changer d’outil de dessin en utilisant des turboboutons
• Utiliser des outils de dessin

Faire le suivi de l’outil de dessin à utiliser


Une application graphique doit pouvoir connaître à tout moment le type d’outil de
dessin (une ligne, un rectangle, une ellipse ou un rectangle arrondi, par exemple)
que l’utilisateur veut utiliser. Vous pouvez affecter des nombres à chaque type
d'outil, mais vous devrez alors vous rappeler de la signification de chaque
nombre. Vous pouvez rendre cette technique plus simple en affectant un nom de
constante mnémonique à chaque nombre, mais le code sera alors incapable de
distinguer les nombres se trouvant dans la bonne plage et ceux du bon type. Par
chance, le Pascal Objet fournit un moyen de gérer ces deux points faibles. Vous
pouvez déclarer un type énuméré.

7-12 Guide du développeur


Présentation de la programmation relative aux graphiques

Un type énuméré est juste un moyen rapide pour affecter des valeurs
séquentielles à des constantes. Depuis qu'il s'agit aussi d'une déclaration de type,
vous pouvez utiliser la vérification de type du Pascal Objet pour vous assurer
que vous n'affectez que ces valeurs spécifiques.
Pour déclarer un type énuméré, utilisez le mot réservé type, suivi par un
identificateur de type, du signe égal et des identificateurs pour les valeurs mis
entre parenthèses et séparés par des virgules.
Par exemple, le code suivant déclare un type énuméré pour tous les outils de
dessin de l’application graphique :
type
TDrawingTool = (dtLine, dtRectangle, dtEllipse, dtRoundRect);
Par convention, les identificateurs de type commencent par la lettre T, et les
constantes similaires (celles constituant le type énuméré) commencent par un même
préfixe de deux caractères (comme ici dt pour “drawing tool”).
La déclaration du type TDrawingTool est équivalente à la déclaration d'un
groupe de constantes :
const
dtLine = 0;
dtRectangle = 1;
dtEllipse = 2;
dtRoundRect = 3;
La principale différence est qu'en déclarant un type énuméré, vous affectez des
constantes et pas seulement des valeurs, mais aussi un type qui permet d'utiliser
la vérification de type du Pascal Objet pour vous prémunir de nombreuses
erreurs. Une variable de type TDrawingTool peut être affectée seulement par une
des constantes dtLine..dtRoundRect. Toute tentative d'affectation d'un autre
nombre (même de la portée 0..3) génèrera une erreur de compilation.
Dans le code suivant, un champ ajouté à une fiche fera le suivi de l’outil de
dessin de la fiche :
type
TDrawingTool = (dtLine, dtRectangle, dtEllipse, dtRoundRect);
TForm1 = class(TForm)
...{ déclarations de méthode}
public
Drawing: Boolean;
Origin, MovePt: TPoint;
DrawingTool: TDrawingTool;{ champ pour l’outil en cours }
end;

Changement d’outil en utilisant un turbobouton


Chaque outil de dessin de votre application doit avoir un gestionnaire pour son
événement OnClick. Supposons que votre application ait une barre d’outils
comportant un bouton pour chacun des quatre outils de dessin : ligne, rectangle,
ellipse et rectangle arrondi. Vous attacherez les gestionnaires suivants aux

Utilisation des graphiques et du multimédia 7-13


Présentation de la programmation relative aux graphiques

événements OnClick des quatre boutons, en affectant à DrawingTool la valeur


correspondant à chaque outil :
procedure TForm1.LineButtonClick(Sender: TObject);{ LineButton }
begin
DrawingTool := dtLine;
end;
procedure TForm1.RectangleButtonClick(Sender: TObject);{ RectangleButton }
begin
DrawingTool := dtRectangle;
end;
procedure TForm1.EllipseButtonClick(Sender: TObject);{ EllipseButton }
begin
DrawingTool := dtEllipse;
end;
procedure TForm1.RoundedRectButtonClick(Sender: TObject);{ RoundRectButton }
begin
DrawingTool := dtRoundRect;
end;

Utilisation des outils de dessin


Vous savez maintenant spécifier l’outil à utiliser. Il vous reste à indiquer
comment dessiner les différentes formes. Les seules méthodes réalisant des
dessins sont les gestionnaires de souris (déplacement de souris et relâchement de
bouton de souris), et le seul code de dessin dessine des lignes, quel que soit
l’outil sélectionné.
Pour utiliser les différents outils de dessin, votre code doit spécifier comment
dessiner selon l’outil sélectionné. Vous devez ajouter l’instruction au gestionnaire
d’événement de chaque outil.
Cette section explique comment :
• dessiner des formes
• partager du code en amont des gestionnaires d’événements

Dessiner des formes


Dessiner des formes est aussi simple que dessiner des lignes. Une seule instruction
suffit. Vous n’avez besoin que des coordonnées.
Voici réécrit le gestionnaire de l’événement OnMouseUp qui dessine des formes
pour les quatre outils :
procedure TForm1.FormMouseUp(Sender: TObject);
begin
case DrawingTool of
dtLine:
begin
Canvas.MoveTo(Origin.X, Origin.Y);
Canvas.LineTo(X, Y)
end;

7-14 Guide du développeur


Présentation de la programmation relative aux graphiques

dtRectangle: Canvas.Rectangle(Origin.X, Origin.Y, X, Y);


dtEllipse: Canvas.Ellipse(Origin.X, Origin.Y, X, Y);
dtRoundRect: Canvas.RoundRect(Origin.X, Origin.Y, X, Y,
(Origin.X - X) div 2, (Origin.Y - Y) div 2);
end;
Drawing := False;
end;
Il est également nécessaire de modifier le gestionnaire de OnMouseMove pour
dessiner des formes :
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
if Drawing then
begin
Canvas.Pen.Mode := pmNotXor;
case DrawingTool of
dtLine: begin
Canvas.MoveTo(Origin.X, Origin.Y);
Canvas.LineTo(MovePt.X, MovePt.Y);
Canvas.MoveTo(Origin.X, Origin.Y);
Canvas.LineTo(X, Y);
end;
dtRectangle: begin
Canvas.Rectangle(Origin.X, Origin.Y, MovePt.X, MovePt.Y);
Canvas.Rectangle(Origin.X, Origin.Y, X, Y);
end;
dtEllipse: begin
Canvas.Ellipse(Origin.X, Origin.Y, X, Y);
Canvas.Ellipse(Origin.X, Origin.Y, X, Y);
end;
dtRoundRect: begin
Canvas.RoundRect(Origin.X, Origin.Y, X, Y,
(Origin.X - X) div 2, (Origin.Y - Y) div 2);
Canvas.RoundRect(Origin.X, Origin.Y, X, Y,
(Origin.X - X) div 2, (Origin.Y - Y) div 2);
end;
end;
MovePt := Point(X, Y);
end;
Canvas.Pen.Mode := pmCopy;
end;
En principe, tout le code répétitif de l’exemple précédent devrait être dans une
routine séparée. La section suivante présente le code relatif au dessin des formes
dans une seule routine pouvant être appelée par tous les gestionnaires
d’événements de souris.

Partage de code entre plusieurs gestionnaires d’événements


Chaque fois que plusieurs gestionnaires d'événements utilisent le même code, vous
rendez l'application plus efficace en plaçant le code répété dans une méthode
partagée par les gestionnaires d'événements.

Utilisation des graphiques et du multimédia 7-15


Présentation de la programmation relative aux graphiques

Pour ajouter une méthode à une fiche,


1 Ajoutez la déclaration de la méthode à l'objet fiche.
Il est possible d'ajouter la déclaration dans les sections public ou private, à la
fin des déclarations de l'objet fiche. Si le code partage uniquement les détails
de la manipulation de certains événements, il est préférable de créer une
méthode partagée private.
2 Ecrivez l'implémentation de la méthode dans la partie implementation de l’unité de
la fiche.
L'en-tête de l'implémentation de la méthode doit correspondre exactement à la
déclaration, les mêmes paramètres apparaissant dans le même ordre.
Le code suivant ajoute à la fiche une méthode appelée DrawShape et l’appelle
depuis chacun des gestionnaires. D’abord, la déclaration de DrawShape est
ajoutée à la déclaration de l'objet fiche :
type
TForm1 = class(TForm)
...{ champs et méthodes déclarés ici}
public
{ déclarations publiques }
procedure DrawShape(TopLeft, BottomRight: TPoint; AMode: TPenMode);
end;
Ensuite l’implémentation de DrawShape est écrite dans la partie implementation de
l’unité :
implementation
{$R *.FRM}
...{ autres implémentations de méthode omises pour plus de clarté }
procedure TForm1.DrawShape(TopLeft, BottomRight: TPoint; AMode: TPenMode);
begin
with Canvas do
begin
Pen.Mode := AMode;
case DrawingTool of
dtLine:
begin
MoveTo(TopLeft.X, TopLeft.Y);
LineTo(BottomRight.X, BottomRight.Y);
end;
dtRectangle: Rectangle(TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y);
dtEllipse: Ellipse(TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y);
dtRoundRect: RoundRect(TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y,
(TopLeft.X - BottomRight.X) div 2, (TopLeft.Y - BottomRight.Y) div 2);
end;
end;
end;

7-16 Guide du développeur


Présentation de la programmation relative aux graphiques

Les autres gestionnaires d'événements sont modifiés pour appeler DrawShape.


procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
DrawShape(Origin, Point(X, Y), pmCopy);{ dessine la forme finale }
Drawing := False;
end;
procedure TForm1.FormMouseMove(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Drawing then
begin
DrawShape(Origin, MovePt, pmNotXor);{ efface la forme précédente }
MovePt := Point(X, Y);{ enregistre le point en cours }
DrawShape(Origin, MovePt, pmNotXor);{ dessine la forme en cours }
end;
end;

Dessiner sur un graphique


Vous n’avez pas besoin de composant pour manipuler les objets graphiques de
votre application. Vous pouvez construire des objets graphiques, dessiner sur
eux, les sauvegarder et les détruire sans même dessiner sur l’écran. En fait, il est
rare qu’une application dessine directement sur une fiche. Le plus souvent, une
application doit dessiner sur un graphique. Elle utilise ensuite un contrôle image
de la VCL pour afficher le graphique sur une fiche.
Une fois les opérations de dessin de l'application reportées sur le graphique du
contrôle image, il est facile d’y ajouter les fonctionnalités relatives à l’impression,
aux opérations sur le Presse-papiers, à l'ouverture et à l'enregistrement des objets
graphiques. Les objets graphiques peuvent être des fichiers bitmap, des
métafichiers, des icônes ou toute autre classe graphique installée en tant que
graphique JPEG.
Remarque Etant donné que vous dessinez sur une image hors écran, comme un canevas
Tbitmap, l’image n’apparaît pas tant qu’un contrôle effectue la copie d’un bitmap
sur le canevas du contrôle. En d’autres mots, lorsque vous dessinez des bitmaps
et les affectez à un contrôle image, l’image n’apparaît que si le contrôle a la
possibilité de traiter son message. En revanche, si vous dessinez directement sur
la propriété Canvas d’un contrôle, l’objet image apparaît immédiatement.

Création de graphiques défilables


Le graphique ne doit pas être de la même taille que la fiche : il doit être soit
plus petit, soit plus grand. En ajoutant un contrôle boîte de défilement sur la
fiche et en plaçant une image graphique à l’intérieur, vous pouvez afficher des
graphiques beaucoup plus grands que la fiche et même plus grands que l’écran.
Pour ajouter un graphique défilable, vous devez commencer par ajouter un
composant TScrollbox puis ajouter ensuite le contrôle image.

Utilisation des graphiques et du multimédia 7-17


Présentation de la programmation relative aux graphiques

Ajout d’un contrôle image


Un contrôle image est un composant conteneur qui vous permet d’afficher vos
objets bitmap. Un contrôle image peut être utilisé pour contenir un bitmap qui
n'est pas nécessairement affiché en permanence, ou un bitmap dont l’application a
besoin pour générer d'autres images.
Remarque Pour des informations sur l’utilisation des graphiques dans des contrôles, voir
“Ajout de graphiques à des contrôles” à la page 6-13 .

Positionnement du contrôle
Un contrôle image peut être placé n'importe où dans une fiche. Pour tirer le
meilleur parti de la capacité d’un contrôle image à ajuster sa taille sur celle de son
image, le seul point à définir est le coin supérieur gauche du contrôle. Si le contrôle
image sert d'emplacement non visible pour un bitmap, il peut être placé n'importe
où dans la fiche, comme un composant non visuel.
Si vous placez le contrôle image en le mettant à l’intérieur de la boîte de
défilement déjà installée dans la zone client de la fiche, vous serez sûr que la boîte
de défilement affichera des barres de défilement pour permettre l’accès aux parties
du dessin n’apparaissant pas à l’écran. Définissez ensuite les propriétés du contrôle
image.

Définition de la taille initiale du bitmap


Lorsqu’un contrôle image est ajouté, il n’existe qu’en tant que conteneur. La
propriété Picture du contrôle image peut être définie en mode conception de
façon à contenir un graphique statique. Mais, le contrôle peut également charger
l’image depuis un fichier pendant l’exécution, comme décrit dans la section
“Chargement et enregistrement de fichiers graphiques” à la page 7-20.
Pour créer un bitmap vide au démarrage de l'application,
1 Attachez un gestionnaire à l'événement OnCreate de la fiche contenant l’image.
2 Créez un objet bitmap, et affectez-le à la propriété Picture.Graphic du contrôle
image.
Dans cet exemple, l’image est dans Form1, la fiche principale de l’application. Le
code attache donc un gestionnaire à l’événement OnCreate de Form1 :
procedure TForm1.FormCreate(Sender: TObject);
var
Bitmap: TBitmap;{ variable temporaire pour contenir le bitmap }
begin
Bitmap := TBitmap.Create;{ construire l’objet bitmap }
Bitmap.Width := 200;{ affecter la largeur initiale... }
Bitmap.Height := 200;{ ...et la hauteur initiale }
Image.Picture.Graphic := Bitmap;{ affecter le bitmap au contrôle image }
end;

7-18 Guide du développeur


Présentation de la programmation relative aux graphiques

L'affectation du bitmap à la propriété Graphic de l’image affecte le bitmap à l'objet


Picture. Celui-ci se chargera donc de détruire le bitmap lorsqu'il ne sera plus utile :
il n'est donc pas nécessaire de détruire manuellement l'objet bitmap. Il est possible
d'affecter un bitmap différent à l’objet Picture (voir la section “Remplacement de
l’image” à la page 7-21). A ce stade, l’image libère l'ancien bitmap et devient
propriétaire du nouveau.
Si vous exécutez l’application maintenant, la zone client de la fiche apparaît
comme une zone blanche représentant le bitmap. Si vous redimensionnez la
fenêtre de sorte que la zone client ne puisse afficher toute l’image, la boîte de
défilement affiche automatiquement des barres de défilement pour permettre la
visualisation du reste de l’image. Mais si vous essayez de dessiner dans l’image,
rien n’apparaît : l’application dessine toujours dans la fiche qui est derrière
l’image et la boîte de défilement.

Dessiner sur un bitmap


Pour dessiner sur un bitmap, utilisez le canevas du contrôle image et attachez les
gestionnaires d’événements de souris aux événements appropriés du contrôle
image. Typiquement, vous devriez utiliser des opérations sur des régions
(rectangles, polylignes et ainsi de suite). Ce sont des méthodes rapides et
efficaces pour dessiner.
Un moyen efficace de dessiner des images lorsque vous avez besoin d’accéder de
manière individuelle aux pixels est d’utiliser la propriété ScanLine du bitmap. Pour
une utilisation plus générale, vous pouvez définir un format de pixels de 24 bits
et traiter le pointeur renvoyé par ScanLine comme un tableau de couleurs RVB.
Vous devrez sinon connaître le format natif de la propriété ScanLine.
Cet exemple explique comment utiliser ScanLine pour extraire des pixels ligne
par ligne.
procedure TForm1.Button1Click(Sender: TObject);
// Cet exemple montre un dessin effectué directement sur le bitmap
var
x,y : integer;
BitMap : TBitMap;
P : PByteArray;
begin
BitMap := TBitMap.create;
try
BitMap.LoadFromFile('C:\Program Files\Borland\Delphi 4\Images\Splash\256color\
factory.bmp');
for y := 0 to BitMap.height -1 do
begin
P := BitMap.ScanLine[y];
for x := 0 to BitMap.width -1 do
P[x] := y;
end;
canvas.draw(0,0,BitMap);
finally
BitMap.free;
end;
end;

Utilisation des graphiques et du multimédia 7-19


Présentation de la programmation relative aux graphiques

Chargement et enregistrement de fichiers graphiques


Des images graphiques n’existant que pour la durée de l’exécution d’une
application sont d’un intérêt limité. Le plus souvent, la même image est utilisée à
chaque exécution, ou bien l’image créée est enregistrée pour une utilisation
ultérieure. Le contrôle image de la VCL facilite le chargement d’une image depuis
un fichier et son enregistrement.
Les composants de la VCL que vous utilisez pour charger, sauvegarder et
remplacer des images graphiques supportent la plupart des formats de
graphiques dont les fichiers bitmap, les métafichiers, les glyphes, et ainsi de
suite. Ils supportent aussi les classes graphiques installables.
Le mécanisme d’ouverture et d’enregistrement des fichiers graphiques est
semblable à celui utilisé pour les autres fichiers et est décrit dans les sections
suivantes :
• Charger une image depuis un fichier
• Enregistrer une image dans un fichier
• Remplacer l’image

Chargement d’une image depuis un fichier


Votre application doit fournir la possibilité de charger une image depuis un
fichier si votre application a besoin de modifier l’image ou si vous voulez la
stocker à l’extérieur de l’application afin qu’un autre utilisateur ou une autre
application puisse la modifier.
Pour charger un fichier graphique dans un contrôle image, appelez la méthode
LoadFromFile de l’objet Picture du contrôle image.
Le code suivant extrait un nom de fichier dans la boîte de dialogue d’ouverture
des fichiers, et charge ensuite ce fichier dans un contrôle image nommé Image :
procedure TForm1.Open1Click(Sender: TObject);
begin
if OpenDialog1.Execute then
begin
CurrentFile := OpenDialog1.FileName;
Image.Picture.LoadFromFile(CurrentFile);
end;
end;

Enregistrement d’une image dans un fichier


L’objet Picture de la VCL peut charger et enregistrer des graphiques sous divers
formats. Vous pouvez créer et recenser vos propres formats de fichiers graphiques
afin que les objets image puissent également les enregistrer et les stocker.
Pour enregistrer le contenu d’un contrôle image dans un fichier, appelez la
méthode SaveToFile de l’objet Picture du contrôle image.

7-20 Guide du développeur


Présentation de la programmation relative aux graphiques

La méthode SaveToFile nécessite de spécifier le nom du fichier de sauvegarde.


L’image est nouvellement créée et n’a pas encore de nom de fichier, ou bien l’image
existe déjà mais l’utilisateur veut l’enregistrer dans un fichier différent. Dans l’un ou
l’autre cas, l’application doit demander à l’utilisateur un nom de fichier avant
l’enregistrement, comme le montre la section suivante.
Les deux gestionnaires d’événements suivants, attachés respectivement aux
éléments de menu Fichier | Enregistrer et Fichier | Enregistrer sous, gèrent
l’enregistrement des fichiers ayant déjà un nom, l’enregistrement des fichiers
n’ayant pas de nom et l’enregistrement des fichiers sous un nouveau nom.
procedure TForm1.Save1Click(Sender: TObject);
begin
if CurrentFile <> '' then
Image.Picture.SaveToFile(CurrentFile){ enregistrer si déjà nommé }
else SaveAs1Click(Sender);{ sinon, obtenir un nom }
end;
procedure TForm1.Saveas1Click(Sender: TObject);
begin
if SaveDialog1.Execute then{ obtenir un nom de fichier }
begin
CurrentFile := SaveDialog1.FileName;{ enregistrer le nom spécifié par l’utilisateur }
Save1Click(Sender);{ puis enregistrer normalement }
end;
end;

Remplacement de l’image
Il est possible à tout moment de remplacer l’image d’un contrôle image. Si un
nouveau graphique est affecté à l’image ayant déjà un graphique, le nouveau
graphique remplace l’ancien.
Pour remplacer l’image contenue dans un contrôle image, affectez un nouveau
graphique à l’objet Picture du contrôle image.
La création d’un nouveau graphique passe par le même processus que la création
du premier graphique (voir la section “Définition de la taille initiale du bitmap” à
la page 7-18), mais il faut également permettre à l’utilisateur de choisir une taille
différente de celle utilisée par défaut pour le graphique initial. Un moyen simple de
proposer une telle option est de présenter une boîte de dialogue comme celle de la
figure 7.1.
Figure 7.1 Boîte de dialogue Dimension bitmap de l’unité BMPDlg.

WidthEdit
HeightEdit

Cette boîte de dialogue particulière est créée dans l’unité BMPDlg incluse dans le
projet GraphEx (répertoire EXAMPLES\DOC\GRAPHEX).

Utilisation des graphiques et du multimédia 7-21


Présentation de la programmation relative aux graphiques

Cette boîte de dialogue étant dans votre projet, ajoutez-la à la clause uses de
l’unité de votre fiche principale. Vous pouvez ensuite attacher un gestionnaire à
l’événement OnClick de l’élément de menu Fichier | Nouveau. Voici un exemple :
procedure TForm1.New1Click(Sender: TObject);
var
Bitmap: TBitmap;{ variable temporaire pour le nouveau bitmap }
begin
with NewBMPForm do
begin
ActiveControl := WidthEdit;{ focalisation sur le champ largeur }
WidthEdit.Text := IntToStr(Image.Picture.Graphic.Width);{ dimensions en cours... }
HeightEdit.Text := IntToStr(Image.Picture.Graphic.Height);{ ...par défaut }
if ShowModal <> idCancel then{ continuer si l’utilisateur n’annule pas la boîte de
dialogue }
begin
Bitmap := TBitmap.Create;{ créer un objet bitmap }
Bitmap.Width := StrToInt(WidthEdit.Text);{ utiliser la largeur spécifiée }
Bitmap.Height := StrToInt(HeightEdit.Text);{ utiliser la hauteur spécifiée }
Image.Picture.Graphic := Bitmap;{ remplacer le graphique par le nouveau bitmap }
CurrentFile := '';{ indique le fichier non nommé }
end;
end;
end;
Remarque L’affectation d’un nouveau bitmap à la propriété Graphic de l’objet Picture oblige
l’objet Picture à détruire le bitmap existant et à devenir propriétaire du nouveau. La
VCL gère automatiquement les détails de la libération des ressources associées à
l’ancien bitmap.

Utilisation du Presse-papiers avec les graphiques


Vous pouvez utiliser le Presse-papiers de Windows pour copier et coller des
graphiques dans les applications ou pour échanger des graphiques avec d’autres
applications. L’objet Clipboard de la VCL facilite la gestion de différents types
d’informations, y compris les graphiques.
Avant d’utiliser l’objet Clipboard dans une application, il faut ajouter l’unité Clipbrd
à la clause uses de n’importe quelle unité devant accéder aux données du Presse-
papiers.

Copier des graphiques dans le Presse-papiers


Toute image graphique, y compris le contenu d’un contrôle image, peut être copiée
dans le Presse-papiers. Une fois placée dans le Presse-papiers, l’image est disponible
pour toutes les applications Windows.
Pour copier une image dans le Presse-papiers, affectez l’image à l’objet Clipboard
en utilisant la méthode Assign.

7-22 Guide du développeur


Présentation de la programmation relative aux graphiques

Le code suivant montre comment copier dans le Presse-papiers l’image d’un


contrôle image nommé Image en réponse au choix d’un élément de menu
Edition|Copier :
procedure TForm1.Copy1Click(Sender: TObject);
begin
Clipboard.Assign(Image.Picture)
end.

Couper des graphiques dans le Presse-papiers


L’opération qui consiste à couper un graphique dans le Presse-papiers est semblable
à la copie, sauf que le graphique est supprimé de la source.
Pour couper un graphique dans le Presse-papiers, commencez par le copier dans le
Presse-papiers, puis supprimez l’original.
Lorsque vous coupez un graphique, la seule question est de savoir comment
montrer que l’image originale a été effacée. La solution classique consiste à mettre
la région à blanc, comme dans le code suivant qui attache un gestionnaire à
l’événement OnClick d’un élément de menu Edition | Couper :
procedure TForm1.Cut1Click(Sender: TObject);
var
ARect: TRect;
begin
Copy1Click(Sender);{ copier l’image dans le Presse-papiers }
with Image.Canvas do
begin
CopyMode := cmWhiteness;
ARect := Rect(0, 0, Image.Width, Image.Height);{ obtenir le rectangle bitmap}
CopyRect(ARect, Image.Canvas, ARect);{ copier le bitmap sur lui-même }
CopyMode := cmSrcCopy;{ restaurer le mode normal }
end;
end;

Coller des graphiques depuis le Presse-papiers


Si le Presse-papiers de Windows contient un graphique bitmap, il est possible de le
coller dans tout objet image, y compris les contrôles image et la surface d’une fiche.
Pour coller un graphique depuis le Presse-papiers,
1 Appelez la méthode HasFormat de l’objet Clipboard pour vérifier si le Presse-
papiers contient bien un graphique.
HasFormat est une fonction booléenne renvoyant True si le Presse-papiers
contient un élément du type spécifié par le paramètre. Pour tester la présence
d’un graphique, il faut transmettre le paramètre CF_BITMAP.
2 Affectez le Presse-papiers à la destination.

Utilisation des graphiques et du multimédia 7-23


Présentation de la programmation relative aux graphiques

Ce code montre comment coller une image depuis le Presse-papiers dans un


contrôle image en réponse à un clic sur un élément de menu Edition | Coller :
procedure TForm1.PasteButtonClick(Sender: TObject);
var
Bitmap: TBitmap;
begin
if Clipboard.HasFormat(CF_BITMAP) then { y a-t-il un bitmap dans le Presse-papiers ? )
begin
Image.Picture.Bitmap.Assign(Clipboard);
end;
end;
Le graphique du Presse-papiers peut provenir de cette application ou y avoir été
copié par une autre application, comme Paintbrush. Dans ce cas, il n’est pas
nécessaire de vérifier le format du Presse-papiers, car le menu Coller serait
indisponible si le Presse-papiers contenait un format non supporté.

Techniques de dessin dans une application


Cette section explique les détails relatifs à l’implémentation de l’effet “rubber
banding” dans une application graphique qui suit les mouvements de la souris au
fur et à mesure que l’utilisateur dessine un graphique en mode exécution. Le code
qui suit provient d’une application exemple située dans le répertoire EXAMPLES\
DOC\GRAPHEX. Cette application dessine des lignes et des formes sur le
canevas d’une fenêtre en réponse à des cliquer-glisser : l’appui sur le bouton de la
souris commence le dessin, le relâchement du bouton termine le dessin.
Pour commencer, cet exemple de code montre comment dessiner sur la surface
d’une fiche principale. Les exemples ultérieurs expliquent comment dessiner sur
un bitmap.
Cette section traite des sujets suivants :
• Comment répondre à la souris
• Ajout d’un champ à un objet fiche pour faire le suivi des actions de la souris
• Amélioration du dessin de ligne

Répondre à la souris
Votre application peut répondre aux actions de la souris : enfoncement du
bouton de la souris, déplacement de la souris et relâchement du bouton de la
souris. Elle peut aussi répondre à un clic (un appui suivi d’un relâchement, sans
déplacer la souris) qui peut être généré par certaines frappes de touches (comme
l’appui sur la touche Entrée dans une boîte de dialogue modale).
Cette section traite des sujets suivants :
• Qu’y a-t’il dans un événement de souris ?
• Réponse à l’action bouton de souris enfoncé
• Réponse à l’action bouton de souris relâché
• Réponse au déplacement de la souris

7-24 Guide du développeur


Présentation de la programmation relative aux graphiques

Qu’y a-t’il dans un événement de souris ?


La VCL possède trois événements de souris : l’événement OnMouseDown,
l’événement OnMouseMove et l’événement OnMouseUp.
Lorsqu’une application de la VCL détecte une action de la souris, elle appelle le
gestionnaire que vous avez défini pour l’événement correspondant, en lui
transmettant cinq paramètres. Les informations contenues dans ces paramètres
permettent de personnaliser la réponse aux événements. Il s’agit des paramètres
suivants :
Tableau 7.4 Paramètres des événements de souris
Paramètre Signification
Sender L'objet ayant détecté l'action de souris.
Button Indique le bouton de la souris impliqué : mbLeft, mbMiddle ou mbRight.
Shift Indique l’état des touches Alt, Ctrl et Maj au moment de l'action de souris.
X, Y Les coordonnées de l'endroit où l'événement a eu lieu.

La plupart du temps, les informations essentielles pour le gestionnaire d’un


événement souris sont les coordonnées mais, dans certains cas, il est utile de tester
le paramètre Button pour déterminer quel bouton de la souris a provoqué
l'événement.
Remarque Delphi utilise le même critère que Microsoft Windows pour déterminer le bouton
enfoncé. Aussi, si vous avez interverti les boutons “primaire” et “secondaire” par
défaut de la souris (pour que le bouton droit de la souris soit le bouton primaire),
un clic du bouton primaire (droit) entraînera une valeur mbLeft pour le paramètre
Button.

Réponse à l’action bouton de souris enfoncé


Lorsque l’utilisateur appuie sur un bouton de la souris, un événement
OnMouseDown est adressé à l’objet situé en dessous du pointeur de la souris.
L’objet peut alors répondre à l’événement.
Pour répondre à une action bouton de souris enfoncé, attachez un gestionnaire à
l’événement OnMouseDown.
La VCL génère un gestionnaire vide pour l’événement souris enfoncée se
produisant sur la fiche :
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
end;
Voici le code affichant du texte à l’endroit où le bouton de la souris a été
enfoncé. Il utilise les paramètres X et Y transmis à la méthode, puis appelle la
méthode TextOut du canevas pour y afficher le texte :

Utilisation des graphiques et du multimédia 7-25


Présentation de la programmation relative aux graphiques

Le code suivant affiche la chaîne 'Ici!' sur une fiche à l’emplacement du clic de la
souris :
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Canvas.TextOut(X, Y, 'Ici!');{ écrire du texte aux coordonnées (X, Y) }
end;
Lorsque l’application est exécutée, le curseur étant sur la fiche, tout appui sur le
bouton de la souris fera apparaître la chaîne "Ici!" au point cliqué. Ce code
définit la position de dessin en cours par les coordonnées du point où l’utilisateur
a enfoncé le bouton de la souris :
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Canvas.MoveTo(X, Y);{ définir la position du crayon }
end;
Désormais, l’enfoncement du bouton de la souris définit la position du crayon et
initialise ainsi le point de départ de la ligne. Pour dessiner une ligne jusqu’au point
où l’utilisateur a relâché le bouton, vous devez répondre à l’événement bouton de
souris relâché.

Réponse à l’action bouton de souris relâché


Un événement OnMouseUp se produit dès que l’utilisateur relâche le bouton de la
souris. L'événement est, en général, adressé à l'objet au-dessus duquel la souris se
trouvait lorsque le bouton a été enfoncé, qui n'est pas nécessairement celui
au-dessus duquel le bouton de la souris est relâché. Cela permet, par exemple, de
dessiner une ligne comme si elle s'étendait au-delà des bords de la fiche.
Pour répondre à une action bouton de souris relâché, définissez le gestionnaire de
l’événement OnMouseUp.
Voici un gestionnaire OnMouseUp qui dessine une ligne jusqu’au point où le
bouton de la souris a été relâché :
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Canvas.LineTo(X, Y);{ dessiner une ligne de PenPos à (X, Y) }
end;
Le code permet à l’utilisateur de dessiner des lignes en cliquant, en déplaçant la
souris, puis en relâchant le bouton. Dans ce cas, l’utilisateur ne voit pas la ligne
tant qu’il ne relâche pas le bouton de la souris.

Réponse au déplacement de la souris


Un événement OnMouseMove se produit périodiquement lorsque l'utilisateur
déplace la souris. L'événement est adressé à l'objet qui était sous le pointeur de la
souris lorsque l'utilisateur a enfoncé le bouton. Cela vous permet de fournir un
retour d’informations à l’utilisateur en dessinant des lignes temporaires au fur et
à mesure que la souris est déplacée.

7-26 Guide du développeur


Présentation de la programmation relative aux graphiques

Pour répondre aux déplacements de la souris, définissez un gestionnaire pour


l’événement OnMouseMove de la fiche. Cet exemple utilise les événements
déplacement de la souris pour dessiner sur la fiche des formes intermédiaires
pendant que l’utilisateur maintient enfoncé le bouton de la souris, offrant ainsi à
l’utilisateur un aperçu de ce qu’il obtiendra. Le gestionnaire de l’événement
OnMouseMove dessine une ligne dans la fiche à l’emplacement de l’événement
OnMouseMove :
procedure TForm1.FormMouseMove(Sender: TObject;Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Canvas.LineTo(X, Y);{ dessiner la ligne à la position en cours }
end;
Avec ce code, le dessin suit le déplacement de la souris sur la fiche, avant même
que le bouton de la souris ne soit enfoncé.
Les événements déplacement de la souris se produisent, même lorsque le bouton de
la souris n’a pas été enfoncé.
Pour déterminer si un bouton de la souris est enfoncé, il est nécessaire d’ajouter un
objet champ à l’objet fiche.

Ajout d’un champ à un objet fiche


Lorsque vous ajoutez un composant à une fiche, Delphi ajoute également un
champ représentant ce composant dans l’objet fiche. Il est ensuite possible de faire
référence à ce composant par le nom de son champ. Vous pouvez également
ajouter vos propres champs en modifiant la déclaration de type dans le fichier
d’en-tête de l’unité de la fiche.
Dans l’exemple suivant, il faut que la fiche détermine si l’utilisateur a enfoncé le
bouton de la souris. Pour cela, un champ booléen a été ajouté dont la valeur est
définie lorsque l’utilisateur enfonce le bouton de la souris.
Pour ajouter un champ à un objet, modifiez la définition de type de l'objet, en
spécifiant l'identificateur du champ et son type après la directive public à la fin de
la déclaration.
Delphi est “propriétaire” de toutes les déclarations placées avant la directive
public : c’est là qu’il place tous les champs qui représentent les contrôles et les
méthodes répondant aux événements.
Le code suivant ajoute dans la déclaration de l’objet fiche un champ de type
Boolean, appelé Drawing. Il ajoute également deux champs afin de stocker les
points de type TPoint : Origin et MovePt.
type
TForm1 = class(TForm)
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormMouseMove(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);

Utilisation des graphiques et du multimédia 7-27


Présentation de la programmation relative aux graphiques

public
Drawing: Boolean;
Origin, MovePt: TPoint;{ champs pour stocker les points }
end;
Nous disposons maintenant d’un champ Drawing permettant de déterminer s’il faut
dessiner. Il faut donc l’initialiser à True lorsque l’utilisateur enfonce le bouton de la
souris et à False lorsqu’il le relâche :
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Drawing := True;{ définir l’indicateur de dessin }
Canvas.MoveTo(X, Y);
end;
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Canvas.LineTo(X, Y);
Drawing := False;{ effacer l’indicateur de dessin }
end;
Ensuite, vous pouvez modifier le gestionnaire de l’événement OnMouseMove de
façon à ne dessiner que si Drawing est à True :
procedure TForm1.FormMouseMove(Sender: TObject;Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Drawing then{ dessiner seulement si l’indicateur de dessin est défini }
Canvas.LineTo(X, Y);
end;
Désormais, le dessin n’est effectué qu’entre les événements bouton de souris
enfoncé et bouton de souris relâché, mais il y a toujours cette ligne brisée qui suit le
déplacement de la souris au lieu d’une belle ligne droite.
Le problème tient à ce qu’à chaque déplacement de la souris, le gestionnaire de
l’événement déplacement de souris appelle LineTo qui déplace la position du
crayon. Par conséquent, le point d’origine de la ligne droite est perdu lorsque le
bouton de la souris est relâché.

Amélioration du dessin des lignes


Maintenant que nous disposons de champs pour garder la trace des divers points, il
est possible d’améliorer le dessin des lignes dans l’application.

Suivi du point d’origine


Lors du dessin d’une ligne, le point de départ de la ligne doit être suivi avec le
champ Origin.

7-28 Guide du développeur


Présentation de la programmation relative aux graphiques

Il faut initialiser Origin avec le point où l’événement bouton de souris enfoncé se


produit. De cette manière, le gestionnaire de l’événement bouton de souris relâché
peut utiliser Origin pour tracer le début de la ligne, comme dans le code :
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Drawing := True;
Canvas.MoveTo(X, Y);
Origin := Point(X, Y);{ enregistrer le début de ligne }
end;
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Canvas.MoveTo(Origin.X, Origin.Y);{ déplacer le crayon au point de départ }
Canvas.LineTo(X, Y);
Drawing := False;
end;
Cette modification permet de redessiner la ligne finale. Mais qu’en est-il des dessins
intermédiaires ?

Suivi des déplacements


Tel qu’est écrit le gestionnaire de l’événement OnMouseMove, il présente
l’inconvénient de dessiner une ligne, non pas depuis sa position d’origine, mais
depuis la position actuelle de la souris. Pour y remédier, il suffit de placer la
position de dessin au point d’origine et de tirer la ligne jusqu’au point en cours :
procedure TForm1.FormMouseMove(Sender: TObject;Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Drawing then
begin
Canvas.MoveTo(Origin.X, Origin.Y);{ déplacer le crayon au point de départ }
Canvas.LineTo(X, Y);
end;
end;
Le code précédent fait le suivi de la position en cours de la souris, mais les lignes
intermédiaires restent affichées et la ligne finale se voit à peine. L’astuce consiste à
effacer chaque ligne avant de tracer la ligne suivante. Cela implique de garder la
trace de son emplacement. C’est la fonction du champ MovePt ajouté préalablement.
Vous devez définir MovePt par le point d’arrivée de chaque ligne intermédiaire, et
utiliser MovePt et Origin pour effacer cette ligne avant de dessiner la suivante :
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Drawing := True;
Canvas.MoveTo(X, Y);
Origin := Point(X, Y);
MovePt := Point(X, Y);
end;

Utilisation des graphiques et du multimédia 7-29


Utilisation du multimédia

procedure TForm1.FormMouseMove(Sender: TObject;Button: TMouseButton;


Shift: TShiftState; X, Y: Integer);
begin
if Drawing then
begin
Canvas.Pen.Mode := pmNotXor;{ utiliser le mode XOR pour dessiner/effacer }
Canvas.MoveTo(Origin.X, Origin.Y);{ déplacer le crayon à l’origine }
Canvas.LineTo(MovePt.X, MovePt.Y);{ effacer l’ancienne ligne }
Canvas.MoveTo(Origin.X, Origin.Y);{ commencer de nouveau à l’origine }
Canvas.LineTo(X, Y);{ dessiner la nouvelle ligne }
end;
MovePt := Point(X, Y);{ enregistrer le point pour le prochain déplacement }
Canvas.Pen.Mode := pmCopy;
end;
Maintenant, un effet satisfaisant est obtenu lorsque la ligne est dessinée. En
modifiant le mode du crayon en pmNotXor, il combine la ligne avec les pixels de
l’arrière-plan. Lorsque la ligne est effacée, les pixels sont en fait ramenés à leur état
antérieur. En remettant le mode du crayon à pmCopy (sa valeur par défaut) après
avoir dessiné les lignes, le crayon est prêt pour le dessin final lorsque le bouton de
la souris est relâché.

Utilisation du multimédia
Delphi vous permet d'ajouter des composants multimédia à vos applications.
Vous pouvez le faire en ajoutant le composant TAnimate de la page Win32 ou le
composant TMediaplayer de la page Système de la palette des composants.
Utilisez le composant animation pour jouer des séquences vidéo silencieuses
dans votre application. Utilisez le composant lecteur multimédia pour jouer des
séquences audio ou vidéo dans une application.
Pour davantage d’informations sur les composants TAnimate et TMediaplayer, voir
l’aide en ligne de la VCL.
Cette section aborde les sujets suivants :
• Ajout de séquences vidéo silencieuses à une application
• Ajout de séquences audio et/ou vidéo à une application

Ajout de séquences vidéo silencieuses à une


application
Le contrôle animation de Delphi vous permet d'ajouter des séquences vidéo
silencieuses à votre application.
Pour ajouter une séquence vidéo silencieuse à une application :
1 Double-cliquez sur l'icône animation dans la page Win32 de la palette des
composants. Cela place automatiquement un contrôle animation dans la fiche
dans laquelle vous voulez afficher la séquence vidéo.

7-30 Guide du développeur


Utilisation du multimédia

2 En utilisant l'inspecteur d'objets, sélectionnez la propriété Name et entrez un


nouveau nom pour votre contrôle animation. Vous utiliserez ce nom pour
appeler le contrôle animation (respectez les conventions standard des
identificateurs de nom Delphi).
Travaillez toujours directement dans l'inspecteur d'objets pour initialiser des
propriétés de conception ou pour créer des gestionnaires d'événements
3 Effectuez l'une des opérations suivantes :
• Sélectionnez la propriété Common AVI et choisissez l'un des AVI proposés
dans la liste déroulante.
• Ou sélectionnez la propriété FileName, cliquez sur le bouton points de
suspension et choisissez un fichier AVI dans l'un des répertoires disponibles
localement ou sur le réseau, puis choisissez Ouvrir dans la boîte de
dialogue Ouvrir AVI.
• Ou sélectionnez une ressource AVI en utilisant les propriétés ResName ou
ResID. Utilisez la propriété ResHandle pour indiquer le module contenant la
ressource identifiée par ResName ou ResID.
Cela charge le fichier AVI en mémoire. Pour afficher à l'écran le premier plan
de la séquence AVI, utilisez la propriété Active ou la méthode Play, puis
affectez la valeur True à la propriété Open
4 Affectez à la propriété Repetitions le nombre spécifiant combien de fois la
séquence AVI doit être jouée. Si cette valeur est nulle, la séquence est répétée
jusqu'à l'appel de la méthode Stop.
5 Faites les autres modifications des propriétés du contrôle animation. Si, par
exemple, vous voulez modifier le premier plan affiché à l'ouverture du
contrôle, affectez le numéro de plan voulu à la propriété StartFrame.
6 Affectez la valeur True à la propriété Active en utilisant la liste déroulante ou
écrivez un gestionnaire d'événement pour exécuter la séquence AVI quand un
événement spécifique a lieu à l'exécution. Par exemple, pour activer la
séquence AVI quand un objet bouton est choisi, écrivez en conséquence le
gestionnaire d'événement OnClick. Vous pouvez également appeler la méthode
Play pour faire jouer la séquence AVI.
Remarque Si vous faites des modifications à la fiche ou à l'un des composants de la fiche
après avoir affecter la valeur True à Active, la propriété Active revient à False et
vous devez la remettre à True. Vous devez donc faire ceci juste avant la
compilation ou à l'exécution.

Exemple d'ajout de séquences vidéo silencieuses


Vous pouvez, par exemple, afficher un logo animé dans le premier écran
apparaissant au démarrage de votre application. Une fois l'affichage du logo
terminé, l'écran disparaît.

Utilisation des graphiques et du multimédia 7-31


Utilisation du multimédia

Pour exécuter cet exemple, créez un nouveau projet et enregistrez le fichier


Unit1.pas sous le nom Frmlogo.pas et le fichier Project1.dpr sous le nom
Logo.dpr. Ensuite :
1 Double-cliquez sur l'icône animation dans la page Win32 de la palette des
composants.
2 En utilisant l'inspecteur d'objets, affectez à sa propriété Name la valeur Logo1.
3 Sélectionnez sa propriété FileName, cliquez sur le bouton points de
suspension (. . .), choisissez le fichier cool.avi dans le répertoire ..\Demos\
Coolstuf. Cliquez ensuite sur le bouton Ouvrir dans la boîte de dialogue
Ouvrir AVI.
Cela charge le fichier cool.avi en mémoire.
4 Positionnez le contrôle animation dans la fiche en cliquant dessus et en le
faisant glisser sur le coin supérieur droit de la fiche.
5 Affectez la valeur 5 à sa propriété Repetitions.
6 Cliquez sur la fiche pour lui attribuer la focalisation et affectez à sa propriété
Name la valeur LogoForm1 et à sa propriété Caption la valeur Fenêtre Logo.
Diminuez la hauteur de la fiche pour y centrer à droite le contrôle animation.
7 Double-cliquez sur l'événement OnActivate et entrez le code suivant qui
exécute la séquence AVI quand la fiche obtient la focalisation à l'exécution :
Logo1.Active := True;
8 Double-cliquez sur l'icône de libellé dans la page Standard de la palette des
composants. Sélectionnez la propriété Caption du composant et entrez
Bienvenue à Cool Images 4.0. Sélectionnez ensuite sa propriété Font, cliquez sur
le bouton points de suspension (. . .) et choisissez dans la boîte de dialogue
Fonte, Style : gras, Taille : 18, Couleur : Navy, puis choisissez OK. Cliquez sur
le contrôle libellé et faites-le glisser pour le centrer dans la fiche.
9 Cliquez sur le contrôle animation pour lui donner la focalisation. Double-
cliquez sur son événement OnStop et écrivez le code suivant pour fermer la
fiche à l'arrêt du fichier AVI :
LogoForm1.Close;
10 Sélectionnez Exécuter|Exécuter pour exécuter la fenêtre au logo animé.

Ajout de séquences audio et/ou vidéo à une


application
Le composant lecteur multimédia de Delphi vous permet d'ajouter des séquences
audio et/ou vidéo à votre application. Il ouvre un périphérique de média et peut
jouer, arrêter, faire une pause, enregistrer, etc., les séquences audio et/ou vidéo
utilisées par le périphérique de média. Le périphérique de média peut être
matériel ou logiciel.

7-32 Guide du développeur


Utilisation du multimédia

Pour ajouter une séquence audio et/ou vidéo à une application :


1 Double-cliquez sur l'icône du lecteur multimédia dans la page Système de la
palette des composants. Cela place automatiquement un contrôle lecteur
multimédia dans la fiche à laquelle vous voulez jouer les caractéristiques
multimédia.
2 En utilisant l'inspecteur d'objets, sélectionnez la propriété Name et entrez le
nouveau nom du contrôle lecteur multimédia. Vous utiliserez ce nom pour
désigner le contrôle lecteur multimédia. Respectez les conventions standard
des identificateurs de nom Delphi).
Travaillez toujours directement dans l'inspecteur d'objets pour initialiser des
propriétés de conception ou pour créer des gestionnaires d'événements.
3 Sélectionnez la propriété DeviceType et choisissez le type de périphérique
approprié ouvert par la propriété AutoOpen ou la méthode Open. Si DeviceType
a la valeur dtAutoSelect, le type de périphérique est sélectionné en fonction de
l'extension du fichier média spécifié par la propriété FileName. Pour davantage
d'informations sur les types de périphériques et leurs fonctions, voir le tableau
suivant.
4 Si le périphérique stocke son média dans un fichier, spécifiez le nom du
fichier média en utilisant la propriété FileName. Sélectionnez la propriété
FileName. Cliquez sur le bouton points de suspension et choisissez un fichier
média dans un répertoire disponible localement ou sur le réseau, puis
choisissez Ouvrir dans la boîte de dialogue Ouvrir. Sinon à l'exécution, insérez
dans le lecteur matériel le support contenant le média (disque, cassette, etc)
pour le périphérique de média sélectionné.
5 Affectez la valeur True à la propriété AutoOpen. Ainsi, le lecteur multimédia
ouvre automatiquement le périphérique spécifié quand la fiche contenant le
lecteur est créée à l'exécution. Si AutoOpen a la valeur False, le périphérique
doit être ouvert par un appel de la méthode Open.
6 Affectez la valeur True à la propriété AutoEnable pour activer ou désactiver
automatiquement à l'exécution les boutons nécessaires du lecteur multimédia.
Sinon, double-cliquez sur la propriété EnabledButtons pour affecter la valeur
True ou False à chaque bouton selon que vous souhaitez l'activer ou pas.
Le périphérique multimédia est exécuté, mis en pause ou arrêté quand
l'utilisateur clique sur les boutons correspondants du composant lecteur
multimédia. Il est également possible de contrôler le périphérique via les
méthodes correspondant aux boutons (Play, Pause, Stop, Next, Previous, etc).
7 Positionnez la barre de contrôle du lecteur multimédia dans la fiche. Vous
pouvez le faire en cliquant dessus et en la faisant glisser à la position de votre
choix ou en sélectionnant la propriété Align et en choisissant l'alignement
souhaité dans sa liste déroulante.
Si vous voulez que le lecteur multimédia soit invisible à l'exécution, affectez la
valeur False à sa propriété Visible et contrôlez le périphérique en appelant les
méthodes appropriées (événement Play, Pause, Stop, Next, Previous, Step, Back,
Start Recording, Eject).

Utilisation des graphiques et du multimédia 7-33


Utilisation du multimédia

8 Effectuez les autres paramétrages du contrôle lecteur multimédia. Si, par


exemple, le média nécessite une fenêtre d'affichage, affectez à la propriété
Display le contrôle affichant le média. Si le périphérique utilise plusieurs
pistes, affectez à la propriété Tracks la piste souhaitée.

Tableau 7.5 Types de périphériques multimédia et leurs fonctions


Utilise Utilise
Type de Logiciel/matériel des une fenêtre
périphérique utilisé Joue pistes d’affichage
dtAVIVideo Lecteur AVI Vidéo pour fichiers AVI Video Non Oui
Window
dtCDAudio Lecteur CD Audio pour Disques CD Audio Oui Non
Windows ou un lecteur
CD Audio
dtDAT Lecteur de cassettes Cassettes audio- Oui Non
audio-numériques numériques
dtDigitalVideo Lecteur vidéo-numérique fichiers AVI, MPG, Non Oui
pour Windows MOV
dtMMMovie Lecteur de films MM films MM Non Oui
dtOverlay Périphérique overlay Vidéo analogique Non Oui
dtScanner Scanner d’image N/d pour Play Non Non
(scanne des images
avec Record)
dtSequencer Séquenceur MIDI pour fichiers MIDI Oui Non
Windows
dtVCR Enregistreur de cassettes Cassettes vidéo Non Oui
vidéo
dtWaveAudio Lecteur audio Wav pour fichiers WAV Non Non
Windows

Exemple d'ajout de séquences audio et / ou vidéo


Cet exemple exécute une séquence vidéo AVI pour une publicité multimédia de
Delphi. Pour exécuter cet exemple, créez un nouveau projet et enregistrez le
fichier Unit1.pas sous le nom FrmAd.pas et le fichier Project1.dpr sous le nom
DelphiAd.dpr. Puis :
1 Double-cliquez sur l'icône lecteur multimédia dans la page Système de la
palette des composants.
2 En utilisant l'inspecteur d'objets, affectez à la propriété Name du lecteur
multimédia la valeur VideoPlayer1.
3 Sélectionnez sa propriété DeviceType et choisissez dtAVIVideo dans la liste
déroulante.
4 Sélectionnez sa propriété FileName, cliquez sur le bouton points de
suspension (. . .) et choisissez le fichier speedis.avi dans le répertoire ..\
Demos\Coolstuf. Choisissez le bouton Ouvrir dans la boîte de dialogue
Ouvrir.speedis.avi

7-34 Guide du développeur


Utilisation du multimédia

5 Affectez la valeur True à sa propriété AutoOpen et la valeur False à sa


propriété Visible.
6 Double-cliquez sur l'icône animation dans la page Win32 de la palette des
composants. Affectez la valeur False à sa propriété AutoSize, 175 à sa
propriété Height et 200 à sa propriété Width. Cliquez sur le contrôle
animation et faites-le glisser dans le coin supérieur gauche de la fiche.
7 Cliquez sur le contrôle lecteur multimédia pour lui donner la focalisation.
Sélectionnez sa propriété Display et choisissez Animate1 dans la liste
déroulante.
8 Cliquez sur la fiche pour lui attribuer la focalisation et sélectionnez sa
propriété Name et affectez-lui la valeur Delphi_Ad. Redimensionnez la fiche
pour lui donner la taille du contrôle animation.
9 Double-cliquez sur l'événement OnActivate et écrivez le code suivant pour
exécuter la séquence vidéo AVI quand la fiche a la focalisation :
Videoplayer1.Play;
10 Choisissez Exécuter|Exécuter pour exécuter la vidéo AVI.

Utilisation des graphiques et du multimédia 7-35


7-36 Guide du développeur
Chapitre

Ecriture d’applications
Chapter 8
8
multithreads
La VCL dispose de plusieurs objets facilitant la conception d’applications
multithreads. Les applications multithreads sont des applications qui contiennent
plusieurs chemins d’exécution simultanés. Même si l’utilisation de plusieurs
threads doit être mûrement réfléchie, elle peut améliorer un programme :
• En évitant les engorgements. Avec un seul thread, un programme doit arrêter
complètement l’exécution pour attendre les processus lents comme les accès
disque, la communication avec d’autres machines ou l’affichage de données
multimédia. La CPU est inactive jusqu’à l’achèvement du processus. Avec
plusieurs threads, l’application peut poursuivre l’exécution dans des threads
séparés pendant qu’un thread attend le résultat du processus lent.
• En organisant le comportement d’un programme. Le comportement d’un
programme peut souvent être décomposé en plusieurs processus fonctionnant
de manière indépendante. L’utilisation de threads permet d’exécuter
simultanément une section de code pour chacun de ces processus. Utilisez les
threads pour assigner des priorités aux diverses tâches du programme afin
d’attribuer davantage de temps machine aux tâches critiques.
• En gérant plusieurs processeurs. Si le système exécutant le programme
dispose de plusieurs processeurs, il est possible d’améliorer les performances
en décomposant le travail en plusieurs threads s’exécutant simultanément sur
des processeurs distincts.
Remarque Tous les systèmes d’exploitation ne gèrent pas réellement l’utilisation de
plusieurs processeurs, même si cela est proposé par le matériel sous-jacent.
Par exemple, Windows 95 ne fait que simuler l’utilisation de processeurs
multiples, même si le matériel sous-jacent le gère.

Ecriture d’applications multithreads 8-1


Définition d’objets thread

Définition d’objets thread


Dans la plupart des applications, vous pouvez utiliser un objet thread pour
représenter un thread d’exécution de l’application. Les objets thread simplifient
l’écriture d’applications multithreads en encapsulant les utilisations les plus
fréquentes des threads.
Remarque Les objets thread ne permettent pas de contrôler les attributs de sécurité ou la
taille de la pile des threads. Si vous souhaitez contrôler ces paramètres, vous
devez utiliser la fonction BeginThread. Même si vous utilisez BeginThread, vous
pouvez néanmoins utiliser les objets de synchronisation de threads et les
méthodes décrites dans la section “Coordination de threads” à la page 8-7. Pour
davantage d’informations sur l’utilisation de BeginThread, voir l’aide en ligne .
Pour utiliser un objet thread dans une application, créez un nouveau descendant
de TThread. Pour créer un descendant de TThread, choisissez Fichier|Nouveau
dans le menu principal. Dans la boîte de dialogue des nouveaux objets,
sélectionnez Objet thread. Vous devez spécifier le nom de classe du nouvel objet
thread. Une fois le nom spécifié, Delphi crée un nouveau fichier unité pour
implémenter le thread.
Remarque A la différence de la plupart des boîtes de dialogue de l’EDI demandant un nom
de classe, la boîte de dialogue Nouvel objet thread ne préfixe pas
automatiquement le nom de classe avec un ‘T’.
Le fichier automatiquement généré contient le squelette du code du nouvel objet
thread. Si vous avez nommé ce thread TMyThread, le code doit avoir l’aspect
suivant :
unit Unit2;
interface
uses
Classes;
type
TMyThread = class(TThread)
private
{ Déclarations privées }
protected
procedure Execute; override ;
end;
implementation
{ TMyThread }
procedure TMyThread.Execute;
begin
{ Code du thread ici }
end;
end.
Vous devez spécifier le code de la méthode Execute. Ces étapes sont décrites
dans les sections suivantes.

8-2 Guide du développeur


Définition d’objets thread

Initialisation du thread
Si vous souhaitez écrire du code d'initialisation pour la nouvelle classe de thread,
vous devez écraser la méthode Create. Ajoutez un nouveau constructeur à la
déclaration de la classe de thread et écrivez le code d'initialisation pour
l'implémenter. C’est là que vous pouvez affecter une priorité par défaut au
thread et indiquer s’il doit être libéré automatiquement à la fin de son exécution.

Affectation d’une priorité par défaut


La priorité indique la préférence accordée au thread quand le système
d’exploitation répartit le temps machine entre les différents threads de l’application.
Utilisez un thread de priorité élevée pour gérer les tâches critiques et un thread de
priorité basse pour les autres tâches. Pour indiquer la priorité de l’objet thread,
affectez la propriétéPriority. Les valeurs possibles de Priority se répartissent sur une
échelle comportant sept valeurs décrite dans le tableau suivant :

Tableau 8.1 Priorités des threads


Valeur Priorité
tpIdle Le thread s’exécute uniquement quand le système est inoccupé.
Windows n’interrompt pas d‘autres threads pour exécuter un thread de
priorité tpIdle.
tpLowest La priorité du thread est deux points en dessous de la normale.
tpLower La priorité du thread est un point en dessous de la normale.
tpNormal Le thread a une priorité normale.
tpHigher La priorité du thread est un point au-dessus de la normale.
tpHighest La priorité du thread est deux points au-dessus de la normale.
tpTimeCritical Le thread a la priorité la plus élevée.

Attention “Gonfler” la priorité du thread pour une opération utilisant intensivement la


CPU peut “sous-alimenter” les autres threads de l'application. Il ne faut accorder
une priorité élevée qu'à des threads qui passent l'essentiel du temps à attendre
des événements extérieurs.
Le code suivant illustre le constructeur d’un thread de priorité basse qui effectue
des tâches d’arrière-plan ne devant pas interférer avec les performances du reste
de l’application :
constructor TMyThread.Create(CreateSuspended: Boolean);
{
inherited Create(CreateSuspended);
Priority := tpIdle;
}

Libération des threads


Généralement, lorsque les threads ont fini d’être exécutés, ils peuvent être
simplement libérés. Dans ce cas, le plus simple consiste à laisser l’objet thread se
libérer lui-même. Pour ce faire, affectez la valeur True à la propriété
FreeOnTerminate.

Ecriture d’applications multithreads 8-3


Définition d’objets thread

Il y a cependant des cas où la fin d’un thread doit être coordonnée avec les
autres threads. Par exemple, il se peut que vous deviez attendre qu’un thread
renvoie une valeur avant d’effectuer une action dans un autre thread. Pour ce
faire, vous ne souhaitez pas libérer le premier thread avant que le second n’ait
reçu la valeur renvoyée. Vous pouvez traiter ce type de situation en affectant la
valeur False à FreeOnTerminate et en libérant explicitement le premier thread à
partir du second.

Ecriture de la fonction thread


La méthode Execute constitue la fonction thread. Vous pouvez la concevoir
comme un programme qui est exécuté par l’application, à cette différence près
qu’il partage le même espace de processus. L’écriture d’une fonction thread est
plus délicate que celle d’un programme distinct car il faut prendre garde à ne pas
écraser la mémoire utilisée par d’autres threads de l’application. D’un autre côté,
comme le thread partage le même espace de processus que les autres threads, il
est possible d’utiliser la mémoire partagée pour faire communiquer les threads.

Utilisation du thread principal VCL


Quand vous utilisez des objets de la hiérarchie des objets VCL, leurs propriétés
et méthodes ne sont pas nécessairement adaptées à l’utilisation de threads. C’est-
à-dire que l’accès aux propriétés et méthodes peut effectuer des actions utilisant
de la mémoire qui n’est pas protégée de l’action d’autres threads. De ce fait, un
thread principal VCL est placé à part pour l’accès aux objets VCL. C’est ce
thread qui gère tous les messages Windows reçus par les composants d’une
application.
Si tous les objets accèdent à leurs propriétés et exécutent leurs méthodes dans ce
seul thread, il n’est pas nécessaire de se préoccuper d’éventuelles interférences
entre les objets. Pour utiliser le thread principal VCL, créez une routine séparée
effectuant les actions nécessaires. Appelez cette routine séparée depuis la
méthode Synchronize de votre thread. Par exemple :
procedure TMyThread.PushTheButton;
begin
Button1.Click;
end;
ƒ
procedure TMyThread.Execute;
begin
ƒ
Synchronize(PushTheButton);
ƒ
end;
Synchronize attend le thread principal VCL pour entrer dans la boucle des
messages puis exécute la méthode qui lui est transmise.

8-4 Guide du développeur


Définition d’objets thread

Remarque Comme Synchronize utilise la boucle des messages, elle ne fonctionne pas dans
les applications consoles. Vous devez utiliser d'autres mécanismes, comme les
sections critiques, pour protéger l'accès aux objets VCL dans les applications
consoles.
Il n’est pas toujours nécessaire d’utiliser le thread principal VCL. Certains objets
sont adaptés aux threads. Il est préférable de ne pas utiliser la méthode
Synchronize quand vous savez que les méthodes d’un objet sont adaptées à
l’utilisation des threads, car cela améliore les performances en évitant d’avoir à
attendre le thread principal VCL pour entrer dans la boucle de messages. Il n’est
pas nécessaire d’utiliser la méthode Synchronize dans les situations suivantes :
• Les composants d’accès aux données sont adaptés aux threads dans la mesure
où chaque thread dispose de son propre composant session de base de données.
Il n’y a qu’une seule exception : les pilotes Access. Ces pilotes sont conçus en
utilisant la bibliothèque ADO Microsoft qui n’est pas adaptée aux threads.
Lorsque vous utilisez des composants d’accès aux données, vous devez
néanmoins encadrer tous les appels aux contrôles orientés données dans la
méthode Synchronize. Ainsi, il est nécessaire de synchroniser les appels qui
relient un contrôle orienté données à un ensemble de données, mais il n’est
pas nécessaire de le faire pour accéder aux données d’un champ de l’ensemble
de données.
Pour davantage d’informations sur l’utilisation de sessions de base de données
avec les threads, voir “Gestion de plusieurs sessions” à la page 16-17.
• Les objets graphiques sont adaptés aux threads. Il n’est pas nécessaire
d’utiliser le thread principal VCL pour accéder aux objets TFont, TPen, TBrush,
TBitmap, TMetafile et TIcon. Il est possible d’accéder aux objets canevas en
dehors de la méthode Synchronize en les verrouillant (voir “Verrouillage
d’objets” à la page 8-7).
• Les listes d’objets ne sont pas adaptées aux threads, mais vous pouvez utiliser
une version adaptée aux threads, TThreadList, à la place de TList.

Utilisation de variables locales aux threads


La méthode Execute et toutes les routines qu’elle appelle ont leurs propres
variables locales comme toute routine Pascal Objet. Ces routines peuvent
également accéder à toutes les variables globales. En fait, les variables globales
constituent un mécanisme puissant de communication entre les threads.
Mais dans certains cas, vous souhaitez utiliser des variables globales pour les
routines du thread sans qu’elles ne soient partagées par les autres instances de la
même classe de thread. Il est possible pour ce faire de déclarer des variables
locales au thread. Déclarez une variable locale au thread en la déclarant dans
une section threadvar. Par exemple :
threadvar
x : integer;
déclare une variable de type entier privée pour chaque thread de l’application,
mais globale à l’intérieur de chaque thread.

Ecriture d’applications multithreads 8-5


Définition d’objets thread

La section threadvar ne peut être utilisée que pour des variables globales. Les
variables pointeur et fonction ne peuvent pas être des variables de thread. Les
types utilisant une sémantique de copie lors de l’écriture, comme les chaînes
longues ne peuvent pas non plus faire office de variables de thread.

Vérification de l’arrêt par d’autres threads


Un thread commence son exécution quand la méthode Execute est appelée (voir
“Exécution d’objets thread” à la page 8-11) et se poursuit jusqu’à l’arrêt de
Execute. Cela correspond à une situation dans laquelle le thread effectue une
tâche spécifique puis s’arrête une fois celle-ci terminée. Dans certains cas, une
application a besoin qu’un thread poursuive son exécution jusqu’à ce qu’un
critère externe soit respecté.
Il est possible de permettre à d’autres threads de signaler qu’il est temps que
votre thread arrête de s’exécuter en testant la propriété Terminated. Quand un
autre thread tente de terminer votre thread, il appelle la méthode Terminate.
Terminate affecte la valeur True à la propriété Terminated de votre thread. C’est à
la méthode Execute de votre thread d’implémenter la méthode Terminate en
testant la valeur de la propriété Terminated. L’exemple suivant illustre une
manière de procéder :
procedure TMyThread.Execute;
begin
while not Terminated do
PerformSomeTask;
end;

Ecriture du code de nettoyage


Vous pouvez centraliser le code de nettoyage lors de la fin de l’exécution du
thread. Juste avant la fin du thread, un événement OnTerminate a lieu. Placez
l’éventuel code de nettoyage dans le gestionnaire d’événement OnTerminate afin
de garantir son exécution quel que soit le chemin d’exécution suivi par la
méthode Execute.
Le gestionnaire d’événement OnTerminate n’est pas exécuté comme partie de
votre thread. Il est en fait exécuté dans le contexte du thread principal VCL de
votre application. Cela a deux implications :
• Il n’est pas possible d’utiliser de variables locales au thread dans un
gestionnaire d’événement OnTerminate (sauf à vouloir utiliser les valeurs du
thread principal VCL).
• Il est possible d’accéder en toute sécurité à tous les composants et objets VCL
dans le gestionnaire d’événement OnTerminate sans se préoccuper des conflits
avec les autres threads.
Pour davantage d’informations sur le thread principal VCL, voir “Utilisation du
thread principal VCL” à la page 8-4.

8-6 Guide du développeur


Coordination de threads

Coordination de threads
Quand vous écrivez le code exécuté lorsque le thread s’exécute, vous devez tenir
compte du comportement des autres threads qui peuvent s’exécuter
simultanément. En particulier, il faut éviter que deux threads tentent d’utiliser
simultanément le même objet ou la même variable globale. De plus, le code d’un
thread peut dépendre de tâches effectuées par d’autres threads.

Eviter les accès simultanés


Pour éviter les conflits avec d’autres threads lors de l’accès à des objets ou des
variables, il peut être nécessaire de bloquer l’exécution des autres threads jusqu’à
ce que le code d’un thread ait terminé une opération. Mais il ne faut pas bloquer
inutilement l’exécution des threads. Cela peut provoquer une dégradation
importante des performances et réduire à néant les avantages liés à l’utilisation
de threads multiples.

Verrouillage d’objets
Certains objets disposent d’un verrouillage intégré qui empêche les autres
threads d’utiliser cette instance d’objet.
Ainsi, les objets canevas (TCanvas et ses descendants) ont une méthode Lock qui
empêche les autres threads d’accéder au canevas jusqu’à l’appel de la méthode
Unlock.
La VCL contient également un objet liste adapté aux threads, TThreadList. L’appel
de TThreadList.LockList renvoie l’objet liste tout en empêchant les autres threads
d’exécution d’utiliser la liste jusqu’à l’appel de la méthode UnlockList. Les appels
des méthodes TCanvas.Lock et TThreadList.LockList peuvent être imbriqués. Le
verrou n’est pas libéré tant que le dernier verrouillage n’est pas associé au
déverrouillage correspondant dans le même thread.

Utilisation de sections critiques


Pour les objets ne disposant pas de verrouillage intégré, vous pouvez utiliser une
section critique. Les sections critiques fonctionnent comment une porte ne
pouvant être franchie que par un seul thread à la fois. Pour utiliser une section
critique, créez une instance globale de TCriticalSection. TCriticalSection dispose de
deux méthodes, Acquire (qui empêche les autres threads d’exécuter la section) et
Release (qui retire le blocage).
Chaque section critique est associée à la mémoire globale devant être protégée.
Chaque thread accédant à cette mémoire globale doit commencer par utiliser la
méthode Acquire pour vérifier qu’un autre thread n’est pas en train de l’utiliser.
Une fois terminé, le thread appelle la méthode Release afin que les autres threads
puissent accéder à la mémoire globale en appelant Acquire.

Ecriture d’applications multithreads 8-7


Coordination de threads

Attention Les sections critiques ne peuvent fonctionner que si tous les threads les utilisent
pour accéder à la mémoire globale associée. Les threads qui ne tiennent pas
compte des sections critiques et accèdent à la mémoire globale sans appeler
Acquire peuvent provoquer des problèmes d’accès simultanés.
Par exemple, une application a une section critique des variables globales,
LockXY, qui bloque l’accès aux variables globales X et Y. Tout thread utilisant X
ou Y doit encadrer cette utilisation d’appels à la section critique comme ci-
dessous :
LockXY.Acquire; { bloque les autres threads }
try
Y := sin(X);
finally
LockXY.Release;
end;

Utilisation du synchronisateur à écriture exclusive et lecture


multiple
Lorsque vous utilisez des sections critiques pour protéger la mémoire globale,
seul un thread peut utiliser la mémoire à un moment donné. Cette protection
peut être exagérée, notamment si un objet ou une variable doit être souvent lu
mais dans lequel vous écrivez très rarement. Il n'y a aucun danger à ce que
plusieurs threads lisent la même mémoire simultanément, pourvu qu’aucun
thread n'y écrit.
Lorsqu’une mémoire globale est souvent lue, mais dans laquelle les threads
n'écrivent qu'occasionnellement, vous pouvez la protéger à l'aide de l'objet
TMultiReadExclusiveWriteSynchronizer. Cet objet agit comme une section critique
qui permet à plusieurs threads de lire la mémoire qu'elle protège à condition
qu'aucun thread n'y écrive. Les threads doivent disposer d'un accès exclusif en
écriture à la mémoire protégée par TMultiReadExclusiveWriteSynchronizer.
Pour utiliser un synchronisateur à écriture exclusive et à lecture multiple, créez
une instance globale de TMultiReadExclusiveWriteSynchronizer associée à la
mémoire globale que vous souhaitez protéger. Chaque thread qui lit cette
mémoire doit au préalable appeler la méthode BeginRead. BeginRead évite qu'un
autre thread n'écrive simultanément dans la mémoire. Lorsqu'un thread a fini de
lire la mémoire protégée, il appelle la méthode EndRead. Tout thread qui écrit
dans la mémoire protégée doit au préalable appeler BeginWrite. BeginWrite évite
qu'un autre thread ne lise ou n'écrive simultanément dans la mémoire. Lorsqu'un
thread a fini d'écrire dans la mémoire protégée, il appelle la méthode EndWrite,
de sorte que les threads en attente puissent commencer à lire la mémoire.
Attention Comme les sections critiques, le synchronisateur à écriture exclusive et à lecture
multiple ne fonctionne que si chaque thread l'utilise pour accéder à la mémoire
globale associée. Les threads qui ignorent le synchronisateur et accèdent à la
mémoire globale sans appeler BeginRead ou BeginWrite génèrent des problèmes
d'accès simultané.

8-8 Guide du développeur


Coordination de threads

Autres techniques de partage de la mémoire


Si vous utilisez la VCL, utilisez le thread principal VCL pour exécuter votre
code. L’utilisation du thread principal VCL garantit que les objets n’accèdent pas
indirectement à de la mémoire utilisée par d’autres objets VCL dans d’autres
threads. Voir “Utilisation du thread principal VCL” à la page 8-4 pour davantage
d’informations sur le thread principal VCL.
Si la mémoire globale n’a pas besoin d’être partagée par plusieurs threads,
envisagez d’utiliser des variables locales aux threads au lieu de variables
globales. En utilisant des variables locales aux threads, votre thread n’a pas
besoin d’attendre ou de bloquer les autres threads. Voir “Utilisation de variables
locales aux threads” à la page 8-5 pour davantage d’informations sur les
variables locales aux threads.

Attente des autres threads


Si votre thread doit attendre la fin d’autres threads pour terminer une tâche,
vous pouvez demander au thread de suspendre son exécution. Vous pouvez
attendre la fin de l’exécution d’un autre thread ou attendre qu’un autre thread
signale qu’il a achevé une tâche.

Attente de la fin d’exécution d’un thread


Pour attendre la fin de l’exécution d’un thread, utilisez la méthode WaitFor de
l’autre thread. WaitFor ne revient que lorsque l’autre thread se termine, soit en
finissant sa propre méthode Execute ,soit à la suite d’une exception. Par exemple,
le code suivant attend qu’un autre thread remplisse un objet liste de threads
avant d’accéder aux objets de la liste :
if ListFillingThread.WaitFor then
begin
with ThreadList1.LockList do
begin
for I := 0 to Count - 1 do
ProcessItem(Items[I]);
end;
ThreadList1.UnlockList;
end;
Dans l’exemple précédent, l’accès aux éléments de la liste ne se fait que lorsque
la méthode WaitFor indique que la liste a été remplie. La valeur renvoyée doit
être affectée par la méthode Execute du thread en attente. Cependant, puisque les
threads appelant WaitFor veulent connaître le résultat de l’exécution du thread, la
méthode Execute ne renvoie pas de valeur. A la place, la méthode Execute
initialise la propriété ReturnValue. ReturnValue est alors renvoyée par la méthode
WaitFor quand elle est appelée par d’autres threads. Les valeurs renvoyées sont
des entiers. Votre application en détermine la signification.

Ecriture d’applications multithreads 8-9


Coordination de threads

Attente de l’achèvement d’une tâche


Parfois, il est nécessaire d’attendre qu’un thread termine une opération au lieu
d’attendre la fin de l’exécution d’un thread particulier. Pour ce faire, utilisez un
objet événement. Les objets événements (TEvent) doivent être créés avec une
portée globale afin qu’ils puissent agir comme des signaux visibles pour tous les
threads.
Quand un thread termine une opération dont dépendent d’autres threads, il
appelle TEvent.SetEvent. SetEvent active le signal afin que les autres threads le
surveillant sachent que l’opération a été achevée. Pour désactiver le signal,
utilisez la méthode ResetEvent.
Par exemple, imaginons une situation dans laquelle vous devez attendre la fin de
l'exécution de plusieurs threads au lieu d'un seul. Comme vous ne savez pas
quel sera le dernier thread, vous ne pouvez pas utiliser la méthode WaitFor de
l'un d'eux. A la place, vous pouvez faire en sorte que chaque thread incrémente
un compteur à la fin de son exécution et que le dernier thread signale
l'achèvement de l'exécution de tous les threads en générant un événement.
Le code suivant montre la fin du gestionnaire d'événement OnTerminate de tous
les threads dont l'exécution doit être achevée. CounterGuard est un objet section
critique global qui évite l'utilisation simultanée du compteur par plusieurs
threads. Counter est une variable globale qui compte le nombre de threads dont
l'exécution est achevée.
procedure TDataModule.TaskThreadTerminate(Sender: TObject);
begin
ƒ
CounterGuard.Acquire; { obtient un verrou sur le compteur }
Dec(Counter); { décrémente la variable globale du compteur }
if Counter = 0 then
Event1.SetEvent; { signale s'il s'agit du dernier thread }
CounterGuard.Release; { déverrouille le compteur }
ƒ
end;
Le thread principal initialise la variable Counter, lance les threads de tâche et
attend le signal indiquant l'achèvement de l'exécution de tous les threads en
appelant la méthode WaitFor. WaitFor attend l’activation du signal pendant une
durée spécifiée et renvoie l’une des valeurs du tableau suivant.

Tableau 8.2 Valeurs renvoyées par WaitFor


Valeur Signification
wrSignaled Le signal de l'objet événement a été activé.
wrTimeout La durée spécifiée s'est écoulée sans que le signal soit défini.
wrAbandoned L'objet événement a été détruit avant l'écoulement de la durée spécifiée.
wrError Une erreur a eu lieu pendant l'attente.

8-10 Guide du développeur


Exécution d’objets thread

Le code suivant montre comment le thread principal lance les threads de tâche et
reprend la main lorsque leur exécution est achevée :
Event1.ResetEvent; { efface l’événement avant de lancer les threads }
for i := 1 to Counter do
TaskThread.Create(False); { crée et lance les threads de tâche }
if Event1.WaitFor(20000) != wrSignaled then
raise Exception;
{ poursuite avec le thread principal. L’exécution de tous les threads de tâche est achevée
}
Remarque Si vous ne voulez pas cesser d’attendre un événement après un délai spécifié,
transmettez à la méthode WaitFor une valeur de paramètre INFINITE. Faites
attention en utilisant INFINITE, car cela peut provoquer le blocage du thread si
le signal attendu n’arrive pas.

Exécution d’objets thread


Une fois une classe thread implémentée en définissant sa méthode Execute, vous
pouvez l’utiliser dans une application pour exécuter le code de sa méthode
Execute. Pour utiliser un thread, créez une instance de la classe thread. L’instance
de thread peut être créée pour un démarrage immédiat ou placée en état
d’attente afin de n’être exécutée qu’avec l’appel de la méthode Resume. Pour
créer un thread s’exécutant immédiatement, affectez la valeur False au paramètre
CreateSuspended du constructeur. Par exemple, la ligne suivante crée un thread et
commence son exécution :
SecondProcess := TMyThread.Create(false); {crée et exécute le thread }
Attention Ne créez pas trop de threads dans une application. Le surcoût lié à la gestion de
plusieurs threads peut influer sur les performances. La limite recommandée est
de 16 threads par processus sur une machine disposant d’un seul processeur.
Cette limite suppose que la plupart de ces threads attendent des événements
externes. Si tous les threads sont actifs, il convient de réduire encore ce nombre.
Vous pouvez créer plusieurs instances du même type de thread pour exécuter
du code parallèle. Vous pouvez, par exemple, démarrer une nouvelle instance
d’un thread en réponse à une action de l’utilisateur, ce qui permet à chaque
thread de générer la réponse attendue.

Redéfinition de la priorité par défaut


Quand le temps machine accordé au thread est lié à la tâche accomplie par le
thread, sa priorité est définie dans le constructeur, comme décrit dans
“Initialisation du thread” à la page 8-3. Par contre, si la priorité du thread varie
en fonction du moment de l’exécution du thread, créez le thread en état
suspendu, affectez sa priorité puis démarrez l’exécution du thread :
SecondProcess := TMyThread.Create(True); { création mais pas exécution }
SecondProcess.Priority := tpLower; { réduire la priorité normale }
SecondProcess.Resume; { exécuter le thread }

Ecriture d’applications multithreads 8-11


Utilisation des threads dans les applications distribuées

Démarrage et arrêt des threads


Un thread peut être démarré et arrêté plusieurs fois avant de terminer son
exécution. Pour interrompre temporairement l’exécution d’un thread, appelez sa
méthode Suspend. Quand il faut reprendre l’exécution du thread, appelez sa
méthode Resume. Suspend augmente un compteur interne, il est donc possible
d’imbriquer les appels aux méthodes Suspend et Resume. L’exécution du thread
ne reprend que si à chaque appel de Suspend correspond un appel de Resume.
Vous pouvez mettre un terme à l’exécution d’un thread en appelant sa méthode
Terminate. Terminate affecte la valeur True à la propriété Terminated du thread. Si
vous avez correctement implémenté la méthode Execute, elle teste
périodiquement la valeur de la propriété Terminated et s’arrête si Terminated a la
valeur True.

Utilisation des threads dans les applications


distribuées
Les applications distribuées soulèvent des problèmes particuliers pour l'écriture
d'applications multithreads. Lorsque vous vous penchez sur la coordination des
threads, vous devez tenir compte de l'impact des autres processus sur les threads
dans votre application.
Généralement, la gestion des threads dans les applications distribuées incombe à
l'application serveur. Lorsque vous écrivez des serveurs, vous devez prendre en
compte la façon dont les requêtes client sont traitées.
Si chaque requête client possède son propre thread, vous devez faire en sorte
que les différents threads client n'interfèrent pas entre eux. En plus des questions
classiques soulevées par la coordination de plusieurs threads, vous devez veiller
à ce que chaque client dispose d'une vue cohérente de votre application. Par
exemple, vous ne pouvez pas utiliser de variables de thread pour stocker des
informations devant persister au terme de plusieurs requêtes client si le client
utilise un thread différent chaque fois qu'il appelle votre application. Lorsque les
clients modifient les valeurs des propriétés d'objet ou des variables globales, ils
affectent non seulement leur propre vue de cet objet ou variable, mais aussi la
vue des autres clients.

Utilisation des threads dans les serveurs de


messagerie
Les serveurs de messagerie reçoivent les messages de requête client, réalisent une
tâche en réponse à ce message et renvoient des messages au client. Il s’agit par
exemple des applications serveur Internet et des services simples que vous
écrivez à l’aide des sockets.

8-12 Guide du développeur


Utilisation des threads dans les applications distribuées

Généralement, lorsque vous écrivez des serveurs de messagerie, chaque message


client obtient son propre thread. A la réception des messages client, l'application
génère un thread pour les gérer. Ce thread s'exécute jusqu'à ce qu'il envoie une
réponse au client, puis s'achève. Vous devez utiliser les variables et objets
globaux avec attention, mais il est assez facile de contrôler la création et
l'exécution des threads car les messages client sont tous reçus et répartis par le
thread d'application principal.

Utilisation des threads avec les objets distribués


Lorsque vous écrivez des serveurs pour les objets distribués, les questions
soulevées par les threads sont plus complexes. A la différence des serveurs de
messagerie, dont un point du code marque la réception et la répartition des
messages, les clients appellent les objets du serveur en sollicitant l'une de leurs
méthodes ou en accédant à l'une de leurs propriétés. De ce fait, il est difficile
pour les applications serveur de générer des threads différents pour chaque
requête client.

Ecriture d'applications (fichiers .EXE)


Lorsque vous écrivez un fichier .EXE qui implémente un ou plusieurs objets
pour des clients distants, les requêtes client se présentent sous la forme de
threads, différemment suivant que les clients accèdent aux objets à l'aide de
COM ou de CORBA.
• Sous COM, les requêtes client font partie de la boucle des messages de
l'application. Cela signifie que tout code qui s'exécute après le démarrage de
la boucle des messages principale de l'application doit être en mesure
d'empêcher les autres threads d'accéder aux objets et à la mémoire globale.
Dans un environnement supportant DCOM, Delphi fait en sorte qu'aucune
requête client ne se produise tant que le code de la partie d'initialisation de
vos unités n'est pas totalement exécuté. Si votre environnement d'exécution ne
supporte pas DCOM, vous devez vous assurer que le code de la partie
d'initialisation de vos unités est adapté aux threads.
• Sous CORBA, vous pouvez choisir un modèle de thread dans l'expert qui
permet de démarrer un nouveau serveur CORBA. Vous pouvez choisir un
modèle à thread unique ou multithread. Dans les deux modèles, chaque
connexion client possède son propre thread. Vous pouvez utiliser des
variables de thread pour les informations qui persistent au terme des appels
client car tous les appels pour un client donné utilisent le même thread. Dans
le modèle à thread unique, seul un thread client accède à une instance d'objet
à un moment donné. Ainsi, bien que vous deviez protéger l'accès à la
mémoire globale, vous êtes sûr que l'accès aux données d'instance de l'objet
(telles que les valeurs de propriétés) ne génère aucun conflit. Dans le modèle
multithread, plusieurs clients peuvent accéder simultanément à votre
application. Si vous partagez les instances d'objet entre plusieurs clients , vous
devez protéger les données globales et les données de l’instance.

Ecriture d’applications multithreads 8-13


Utilisation des threads dans les applications distribuées

Ecriture de bibliothèques
Lorsqu’une bibliothèque active implémente l'objet distribué, l'utilisation des
threads est généralement contrôlée par la technologie (COM, DCOM ou MTS)
qui gère les appels d'objet. Lorsque vous créez votre bibliothèque serveur avec
l'expert approprié, vous êtes invité à spécifier un modèle de thread qui
détermine l'attribution des threads aux requêtes client. Ces modèles sont les
suivants :
• Modèle à thread unique. Les requêtes client sont sérialisées par le mécanisme
d'appel. Votre fichier .DLL n'est pas concerné par la gestion du thread car il
reçoit une seule requête client à un moment donné.
• Modèle appartement à thread unique. Chaque objet instancié par un client
n'est accessible que par un thread à un moment donné. Vous devez empêcher
l'accès à la mémoire globale par plusieurs threads, mais les données d'instance
(comme les propriétés d'objet) sont adaptées aux threads. De plus, chaque
client accède toujours à l'instance d'objet à l'aide du même thread, ce qui vous
permet d'utiliser des variables de thread.
• Modèle activité. Chaque instance d'objet n'est accessible que par un thread à
un moment donné mais les clients n'utilisent pas toujours le même thread
pour chaque appel. Les données d'instance sont adaptées aux threads, mais
vous devez protéger la mémoire globale, et les variables globales ne sont pas
cohérentes d'un appel client à l'autre.
• Modèle appartement à thread multiple. Chaque instance d'objet peut être
appelée par plusieurs threads simultanément. Vous devez protéger les
données d'instance ainsi que la mémoire globale. Les variables de thread ne
sont pas cohérentes d'un appel client à l'autre.
• Modèle appartement à thread unique ou multiple. Il s'agit du même modèle
que du modèle appartement à thread multiple à la différence que les rappels
émanant des clients s'exécutent dans le même thread. Ainsi, vous n'avez pas
besoin de protéger les valeurs fournies comme paramètres de fonctions de
rappel (callback).
Remarque Généralement, un expert assigne un modèle de thread à votre objet. Lorsque
vous ajoutez plusieurs objets COM à un fichier EXE, l'application s'initialise sous
COM avec le niveau indiqué de prise en charge des threads le plus élevé (le
niveau à thread unique est le plus faible et le niveau à thread unique ou
multiple est le plus élevé). Vous pouvez manuellement modifier la façon dont
votre application initialise la prise en charge des threads sous COM en
changeant la variable globale CoInitFlags dans le fichier source principal du
programme avant l'appel à Application.Intitialize.
Les systèmes basés sur COM utilisent la boucle des messages de l'application
pour synchroniser les threads dans tous les modèles (excepté le modèle
appartement à thread multiple, uniquement disponible sous DCOM). Vous devez
donc vous assurer que tout appel long opéré par le biais d'une interface COM
sollicite la méthode ProcessMessages de l'objet d'application. A défaut, les autres
clients ne peuvent pas accéder à votre application et font de votre bibliothèque
une bibliothèque à thread unique.

8-14 Guide du développeur


Débogage d’applications multithreads

Débogage d’applications multithreads


Lors du débogage d’applications multithreads, il est compliqué de surveiller
l’état de tous les threads s’exécutant simultanément ou même de déterminer quel
thread s’exécute quand vous êtes sur un point d’arrêt. Vous pouvez utiliser la
boîte d’état des threads pour surveiller et manipuler tous les threads de
l’application. Pour afficher la boîte de dialogue Etat des threads, choisissez Voir|
Threads dans le menu principal.
Quand un événement de débogage a lieu, (point d’arrêt, exception, pause), la
vue Etat des threads indique l’état de chaque thread. Cliquez avec le bouton
droit de la souris dans la boîte de dialogue Etat des threads pour accéder aux
commandes permettant d’accéder au code source correspondant ou changer le
thread courant. Quand un thread est marqué comme en cours, l’étape suivante
ou l’opération d’exécution suivante se fait relativement à ce thread.
La boîte de dialogue Etat des threads liste tous les threads d’exécution de
l’application par leur identificateur de thread. Si vous utilisez des objets thread,
l’identificateur de thread correspond à la valeur de la propriété ThreadID. Si vous
n’utilisez pas d’objets thread, l’identificateur de chaque thread est renvoyé lors
de l’appel de BeginThread.
Pour davantage d’informations sur la boîte d’état des threads, voir l’aide en
ligne.

Ecriture d’applications multithreads 8-15


8-16 Guide du développeur
Chapitre

Utilisation des paquets


Chapter 9
9
et des composants
Un paquet est une bibliothèque liée dynamiquement spéciale, utilisée par les
applications Delphi, l’EDI ou les deux. Les paquets d'exécution fournissent des
fonctionnalités lorsqu'un utilisateur exécute une application. Les paquets de
conception sont utilisés pour installer des composants dans l’EDI et pour créer des
éditeurs de propriétés particuliers pour des composants personnalisés. Un même
paquet peut fonctionner à la fois en conception et en exécution, les paquets de
conception faisant souvent appel à des paquets d'exécution. Pour les distinguer
des autres DLL, les paquets sont stockés dans des fichiers dont l'extension
est .BPL (Borland Package Library).
Comme les autres bibliothèques d’exécution, les paquets contiennent du code
pouvant être partagé par plusieurs applications. Par exemple, les composants
Delphi les plus couramment utilisés se trouvent dans un paquet appelé VCL50.
Chaque fois que vous créez une application, vous pouvez spécifier qu'elle utilise
VCL50. Lorsque vous compilez une application créée de cette manière, l'image
exécutable de l’application ne contient que son propre code et ses propres
données, le code commun étant dans VCL50.BPL. Un ordinateur sur lequel sont
installées plusieurs applications utilisant des paquets n'a besoin que d'une seule
copie de VCL50.BPL, qui est partagé par toutes les applications et par l'EDI même.
Delphi est livré avec plusieurs paquets d'exécution précompilés, dont VCL50, qui
encapsulent les composants VCL. Delphi utilise également des paquets de
conception pour faciliter la manipulation des composants dans l'EDI.
Vous pouvez construire des applications avec ou sans paquets. Mais, si vous
voulez ajouter à l'EDI des composants personnalisés, vous devez les installer en
tant que paquets de conception.
Vous pouvez créer vos propres paquets d'exécution afin de les partager entre
plusieurs applications. Si vous écrivez des composants Delphi, compilez-les dans
des paquets de conception avant de les installer.

Utilisation des paquets et des composants 9-1


Pourquoi utiliser des paquets ?

Pourquoi utiliser des paquets ?


Les paquets de conception simplifient la distribution et l'installation de
composants personnalisés. L'utilisation, optionnelle, des paquets d'exécution offre
plusieurs avantages par rapport à la programmation conventionnelle. En
compilant dans une bibliothèque d'exécution du code réutilisé, vous pouvez le
partager entre plusieurs applications. Par exemple, toutes vos applications, y
compris Delphi, peuvent accéder aux composants standard par le biais des
paquets. Comme les applications n'intègrent pas de copies séparées de la
bibliothèque des composants dans leur exécutable, ces dernières sont plus petites,
ce qui économise à la fois de l’espace disque et des ressources système. De plus,
les paquets permettent d’accélérer la compilation car seul le code spécifique à
l’application est compilé à chaque génération.

Les paquets et les DLL standard


Créez un paquet quand vous voulez qu’un composant personnalisé soit utilisable
dans l’EDI. Créez une DLL standard quand vous voulez générer une
bibliothèque utilisable par toute application Windows, quel que soit l’outil de
développement utilisé pour la créer.
Le tableau suivant donne la liste des types de fichiers associés aux paquets.

Tableau 9.1 Fichiers paquet compilés


Extension
du fichier Contenu
.DPK Le fichier source donnant la liste des unités contenues dans le paquet.
DCP Une image binaire contenant une en-tête de paquet et la concaténation de
tous les fichiers DCU du paquet, y compris les informations de symbole
nécessaires au compilateur. Un seul fichier DCP est créé pour chaque
paquet. Le nom de base pour le DCP est celui du fichier source DPK.
Vous devez avoir un fichier .DCP pour construire une application avec
des paquets.
DCU Une image binaire pour un fichier unité contenu dans un paquet.
Lorsque cela est nécessaire, un seul fichier DCU est créé pour chaque
fichier unité.
BPL Le paquet d’exécution. Ce fichier est une DLL avec des caractéristiques
spécifiques à Delphi. Le nom de base du BPL est celui du fichier source
DPK.

Remarque Les paquets partagent leurs données globales avec les autres modules d’une
application.
Pour plus d’informations sur les paquets et les DLL, voir le guide du langage Pascal
Objet.

9-2 Guide du développeur


Paquets d’exécution

Paquets d’exécution
Les paquets d'exécution sont déployés avec les applications Delphi. Ils
fournissent des fonctionnalités lorsqu'un utilisateur exécute une application.
Pour exécuter une application utilisant des paquets, le fichier .EXE de
l’application et tous les fichiers paquet (fichiers .BPL) qu’elle utilise doivent se
trouver sur l'ordinateur. Les fichiers .BPL doivent être dans le chemin du
système pour qu’une application puisse les utiliser. Quand vous déployez une
application, vérifiez que les utilisateurs possèdent la version correcte de chaque
BPL nécessaire.

Utilisation des paquets dans une application


Pour utiliser des paquets dans une application :
1 Chargez ou créez un projet dans l’EDI.
2 Choisissez Projet|Options.
3 Choisissez la page Paquets.
4 Cochez la case “Construire avec les paquets d'exécution” et saisissez un ou
plusieurs noms de paquets dans la boîte de saisie placée en dessous. Les
paquets d’exécution associés avec les paquets de conception déjà installés
apparaissent déjà dans la boîte de saisie. Pour ajouter un paquet à une liste
existante, cliquez sur le bouton Ajouter puis entrez le nom du nouveau paquet
dans la boîte de dialogue Ajout de paquet d'exécution. Pour parcourir la liste
des paquets disponibles, cliquez sur le bouton Ajouter puis sur le bouton
Parcourir placé à côté de la boîte de saisie Nom de paquet dans la boîte de
dialogue Ajout de paquet d'exécution.
Si vous modifiez la boîte de saisie Chemin de recherche dans la boîte de
dialogue Ajout de paquet d'exécution, le chemin d’accès global à la
bibliothèque Delphi est modifié.
Il n'est pas nécessaire d'inclure l'extension dans le nom de paquet. Si vous
tapez les noms directement dans la boîte de saisie Paquets d'exécution,
séparez-les par des points-virgules. Par exemple :
VCL50;VCLDB50;VCLDBX50
Les paquets énumérés dans la boîte de saisie Paquets d'exécution sont
automatiquement liés à l’application lors de sa compilation. Les paquets en
double sont ignorés et si la boîte de saisie est vide, l’application est compilée
sans paquet.
Les paquets d'exécution sont sélectionnés uniquement pour le projet en cours.
Pour que les choix en cours deviennent des choix par défaut pour les futurs
projets, cochez la case Défaut, en bas de la boîte de dialogue.

Utilisation des paquets et des composants 9-3


Paquets d’exécution

Remarque Lorsque vous créez une application avec des paquets, vous devez inclure les
noms originels des unités Delphi dans la clause uses des fichiers source. Par
exemple, le fichier source de la fiche principale pourrait commencer ainsi :
unit MainForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
Chacune des unités référencée dans cet exemple est contenu dans le paquet
VCL50. Néamoins, vous devez conserver ces références dans la clause uses,
même si vous utilisez VCL50 dans votre application, ou vous aurez des erreurs
de compilation. Dans les fichiers source générés, Delphi ajoute automatiquement
ces unités à la clause uses.

Paquets chargés dynamiquement


Pour charger un paquet à l’exécution, appelez la fonction LoadPackage. Par
exemple, le code suivant est exécuté lorsqu’un fichier est choisi dans la boîte de
dialogue de sélection des fichiers.
with OpenDialog1 do
if Execute then
with PackageList.Items do
AddObject(FileName, Pointer(LoadPackage(Filename)));
Pour décharger un paquet dynamiquement, appelez UnloadPackage. Soyez
prudent en détruisant toute instance de classe définie dans le paquet et en
dérecensant les classes précédemment recensées.

Choix des paquets d’exécution à utiliser


Delphi est livré avec plusieurs paquets d'exécution précompilés, dont VCL50, qui
fournissent le langage de base et le support des composants.
Le paquet VCL50 contient les composants les plus couramment utilisés, les
fonctions système et les éléments de l'interface Windows. Il ne comprend pas les
composants base de données ou Windows 3.1, qui se trouvent dans des paquets
distincts. Le tableau suivant liste quelques paquets d'exécution fournis avec
Delphi et les unités qu'ils contiennent.
Tableau 9.2 Paquets d’exécution
Paquet Unités
VCL50.BPL Ax, Buttons, Classes, Clipbrd, Comctrls, Commctrl, Commdlg, Comobj,
Comstrs, Consts, Controls, Ddeml, Dialogs, Dlgs, Dsgnintf, Dsgnwnds,
Editintf, Exptintf, Extctrls, Extdlgs, Fileintf, Forms, Graphics, Grids, Imm,
IniFiles, Isapi, Isapi2, Istreams, Libhelp, Libintf, Lzexpand, Mapi, Mask,
Math, Menu, Messages, Mmsystem, Nsapi, Ole2I, Oleconst, Olectnrs,
Olectrls, Oledlg, Penwin, Printers, Proxies, Registry, Regstr, Richedit,
Shellapi, Shlobj, Stdctrls, Stdvcl, Sysutils, Tlhelp32, Toolintf, Toolwin,
Typinfo, Vclcom, Virtintf, Windows, Wininet, Winsock, Winspool, Winsvc

9-4 Guide du développeur


Paquets d’exécution

Tableau 9.2 Paquets d’exécution (suite)


Paquet Unités
VCLX50.BPL Checklst, Colorgrd, Ddeman, Filectrl, Mplayer, Outline, Tabnotbk, Tabs
VCLDB50.BPL Bde, Bdeconst, Bdeprov, Db, Dbcgrids, Dbclient, Dbcommon, Dbconsts,
Dbctrls, Dbgrids, Dbinpreq, Dblogdlg, Dbpwdlg, Dbtables, Dsintf,
Provider, SMintf
VCLDBX50.BPL Dblookup, Report
DSS50.BPL Mxarrays, Mxbutton, Mxcommon, Mxconsts, Mxdb, Mxdcube, Mxdssqry,
Mxgraph, Mxgrid, Mxpivsrc, Mxqedcom, Mxqparse, Mxqryedt, Mxstore,
Mxtables, Mxqvb
QRPT50.BPL Qr2const, Qrabout, Qralias, Qrctrls, Qrdatasu, Qrexpbld, Qrextra, Qrprev,
Qrprgres, Qrprntr, Qrqred32, Quickrpt
TEE50.BPL Arrowcha, Bubblech, Chart, Ganttch, Series, Teeconst, Teefunci, Teengine,
Teeprocs, Teeshape
TEEDB50.BPL Dbchart, Qrtee
TEEUI50.BPL Areaedit, Arrowedi, Axisincr, Axmaxmin, Baredit, Brushdlg, Bubbledi,
Custedit, Dbeditch, Editchar, Flineedi, Ganttedi, Ieditcha, Pendlg, Pieedit,
Shapeedi, Teeabout, Teegally, Teelisb, Teeprevi, Teexport
VCLSMP50.BPL Sampreg, Smpconst

Ainsi, pour créer une application de base de données client/serveur utilisant des
paquets, vous avez besoin d'au moins deux paquets d'exécution : VCL50 et
VCLDB50. Si vous voulez également utiliser dans votre application des
composants arborescence, vous avez besoin en plus de VCLX50. Pour utiliser ces
paquets, choisissez Projet|Options, sélectionnez la page Paquets et entrez la liste
suivante dans la boîte de saisie Paquets d'exécution.
VCL50;VCLDB50;VCLX50
Il n’est, en fait, pas nécessaire de préciser VCL50, car VCL50 est référencée dans
la clause Requires de VCLDB50 (voir “La clause Requires” à la page 9-10). Votre
application se compilera de la même façon que VCL50 figure ou non dans la
liste.

Paquets personnalisés
Un paquet personnalisé est soit une BPL que vous programmez et compilez
vous-même, soit un paquet précompilé développé par un fournisseur tiers. Pour
utiliser dans une application un paquet d'exécution personnalisé, choisissez
Projet|Options et ajoutez le nom du paquet à la boîte de saisie Paquets
d'exécution de la page Paquets. Par exemple, si vous avez créé un paquet
effectuant des statistiques, nommé STATS.BPL, la boîte de saisie Paquets
d'exécution doit avoir la forme :
VCL50;VCLDB50;STATS
Si vous créez vos propres paquets, vous pouvez les ajouter selon vos besoins à la
liste.

Utilisation des paquets et des composants 9-5


Paquets de conception

Paquets de conception
Des paquets de conception sont utilisés pour installer des composants dans la
palette des composants de l’IDE ou pour créer les éditeurs de propriétés
spéciaux de composants personnalisés.
Delphi est livré avec les paquets composant de conception suivants, déjà installés
dans l'EDI.

Tableau 9.3 Paquets de conception


Paquet Pages de la palette des composants
DCLSTD50.BPL Standard, Supplément, Système, Win32, Dialogues
DCLTEE50.BPL Supplément (composant TChart)
DCLDB50.BPL AccèsBD, ContrôleBD
DCLMID50.BPL AccèsBD (MIDAS)
DCL31W50.BPL Win 3.1
DCLNET50.BPL Internet
NMFAST50.BPL
DCLSMP50.BPL Exemples
DCLOCX50.BPL ActiveX
DCLQRT50.BPL QReport
DCLDSS50.BPL Decision Cube
IBSMP50.BPL Exemples (composant IBEventAlerter)
DCLINT50.BPL (Outils internationaux — Expert DLL ressource)
RCEXPERT.BPL (Expert ressource)
DBWEBXPRT.BPL (Expert Web)

Ces paquets de conception fonctionnent en appelant des paquets d’exécution


référencés dans leur clause Requires. Voir “La clause Requires” à la page 9-10.
Par exemple, DCLSTD50 référence VCL50. DCLSTD50 contient lui-même des
fonctionnalités supplémentaires qui rendent la plupart des composants standard
disponibles dans la palette des composants.
Outre les paquets pré-installés, vous pouvez installer dans l’EDI vos propres
paquets de composants ou ceux développés par des tiers. Le paquet de
conception DCLUSR50 est fourni en guise de conteneur par défaut des nouveaux
composants.

Installation de paquets de composants


Dans C++Builder, tous les composants installés dans l’EDI le sont sous la forme
de paquets. Si vous écrivez vos propres composants, créez et compilez un paquet
les contenant, voir “Création et modification de paquets” à la page 9-8. Le code
source des composants doit respecter le modèle décrit dans la partie IV,
“Création de composants personnalisés”.

9-6 Guide du développeur


Paquets de conception

Pour installer ou désinstaller vos propres composants ou les composants fournis


par un tiers, procédez de la manière suivante :
1 Si vous installez un nouveau paquet, copiez ou déplacez les fichiers paquet
dans un répertoire local. Si le paquet est livré avec des .BPL, .DCP, and .DCU,
vous devez tous les copier. Pour davantage d’informations sur ces fichiers,
voir “Fichiers paquets créés lors d’une compilation réussie” à la page 9-13.
Le répertoire dans lequel sont stockés le fichier .DCP et les fichiers.DCU, s’ils
font partie de la distribution, doit être dans le chemin de la bibliothèque
C++Builder.
Si le paquet est distribué sous forme d’un fichier .DPC (collection de paquet),
seul cet unique fichier doit être copié ; le fichier .DPC contient les autres
fichiers. Pour plus d’informations sur les fichiers de collection de paquet, voir
“Fichiers de collection de paquet” à la page 9-14.)
2 Choisissez Composant|Installer des paquets dans le menu de l’EDI ou
choisissez Projet|Options et cliquez sur l’onglet Paquets.
3 Une liste des paquets disponibles apparaît sous “Paquets de conception”.
• Pour installer un paquet dans l’EDI, cochez la case à cocher placée à côté
du nom de paquet.
• Pour désinstaller un paquet, désélectionnez la case à cocher.
• Pour voir une liste des composants inclus dans un paquet installé,
sélectionnez le paquet et cliquez sur Composants.
• Pour ajouter un paquet à la liste, cliquez sur le bouton Ajouter puis
recherchez dans la boîte de dialogue d’ouverture de paquet le répertoire
dans lequel se trouve le fichier .BPL ou .DPC (voir l’étape 1). Sélectionnez
le fichier .BPL ou .DPC et cliquez sur Ouvrir. Si vous sélectionnez un
fichier .DPC, une nouvelle boîte de dialogue apparaît pour la gestion de
l’extraction du fichier .BPL et d’autres fichiers de la collection de paquets.
Pour retirer un paquet de la liste, sélectionnez le paquet, puis cliquez sur
Supprimer.
4 Cliquez sur OK.
Les composants du paquet sont installés sur la page de la palette des
composants spécifiée dans la procédure RegisterComponents des composants, avec
les noms dont ils étaient affectés dans cette même procédure.
Sauf si vous changez les options par défaut, les nouveaux projets sont créés avec
tous les paquets disponibles installés. Si vous voulez que vos choix d'installation
deviennent les options par défaut pour les nouveaux projets, cochez Défaut, en
bas de la boîte de dialogue.
Pour supprimer des composants de la palette des composants sans désinstaller
de paquet, sélectionnez Composant|Configurer la palette, ou bien Outils|
Options d'environnement et cliquez sur l'onglet Palette. La page Palette contient
la liste de tous les composants installés avec le nom de la page où chacun
apparaît. Sélectionnez le composant à supprimer de la palette et cliquez sur
Cacher.

Utilisation des paquets et des composants 9-7


Création et modification de paquets

Création et modification de paquets


Pour créer un paquet, il faut:
• Un nom pour le paquet.
• Une liste des autres paquets requis (liés) par le nouveau paquet.
• Une liste des fichiers unité devant être contenus par le paquet lors de sa
compilation. Un paquet est avant tout un conteneur pour ses unités de code
source qui contiennent les fonctionnalités du BPL compilé. C’est dans la clause
Contains que vous placez les unités de code source des composants
personnalisés devant être compilés dans un paquet
Les fichiers source de paquets, qui se terminent par l’extension .DPK, sont
générés par l’éditeur de paquets.

Création d’un paquet


Pour créer un paquet, suivez les étapes suivantes. Pour davantage d’informations
sur les étapes suivantes, voir “Présentation de la structure d’un paquet” à la
page 9-10.
1 Choisissez Fichier|Nouveau, sélectionnez l’icône Paquet et choisissez OK.
2 Le paquet généré est affiché dans l’éditeur de paquet.
3 L’éditeur de paquet affiche pour le nouveau paquet un noeud Requires et un
nœud Contains.
4 Pour ajouter une unité à la clause contains, cliquez sur le turbobouton Ajouter
au paquet. Dans la page Ajouter unité, tapez un nom de fichier .PAS dans la
boîte de saisie Nom de fichier unité, ou cliquez sur Parcourir... pour
rechercher le fichier, puis cliquez sur OK. L’unité sélectionnée apparaît sous le
noeud Contains de l’éditeur de paquet. Vous pouvez ajouter des unités
supplémentaires en répétant cette étape.
5 Pour ajouter un paquet à la clause requires, cliquez sur le turbobouton
Ajouter au paquet. Dans la page Nécessite, tapez un nom de fichier .DCP
dans la boîte de saisie Nom de paquet, ou cliquez sur Parcourir... pour
rechercher le fichier, puis cliquez sur OK. Le paquet sélectionné apparaît sous
le noeud Requires dans l’éditeur de paquet. Vous pouvez ajouter des paquets
supplémentaires en répétant cette étape.
6 Cliquez sur le turbobouton Options, et sélectionnez le type de paquet à générer.
• Pour créer un paquet de conception uniquement (un paquet ne pouvant
s’utiliser à l’exécution), sélectionnez le bouton radio Seulement en
conception. (Ou ajoutez la directive de compilation {$DESIGNONLY} au
fichier DPK.)
• Pour créer un paquet d’exécution uniquement (un paquet ne pouvant être
installé), sélectionnez le bouton radio d’exécution seule. (Ou ajoutez la
directive de compilation {$RUNONLY} au fichier DPK.)

9-8 Guide du développeur


Création et modification de paquets

• Pour créer un paquet utilisable à l’exécution et à la conception, sélectionnez


les deux boutons radio Conception et Exécution.
7 Dans l’éditeur de paquet, cliquez sur le turbobouton Compiler le paquet pour
compiler votre paquet.

Modification d’un paquet existant


Il y a plusieurs manières d’ouvrir un paquet existant afin de le modifier :
• Choisissez Fichier|Ouvrir (ou Fichier|Réouvrir) et sélectionnez un fichier DPK.
• Choisissez Composant|Installer des paquets, sélectionnez un paquet dans la
liste Paquets de conception et choisissez le bouton Modifier.
• Quand l’éditeur de paquet est ouvert, sélectionnez un des paquets du noeud
Requires, cliquez avec le bouton droit de la souris et choisissez Ouvrir.
Pour modifier la description d’un paquet ou définir les options d’utilisation,
cliquez sur le turbobouton Options dans l’éditeur de paquet et sélectionnez
l’onglet Description.
La boîte de dialogue Options du projet possède une case à cocher Défaut dans le
coin inférieur gauche. Si vous cliquez sur OK quand cette case est cochée, les
options choisies sont enregistrées comme paramètres par défaut pour les
nouveaux projets . Pour restaurer les valeurs par défaut originales, supprimez ou
renommez le fichier DEFPROJ.DOF.

Modification manuelle de fichiers source de paquets


Les fichiers source de paquets, comme les fichiers projet, sont générés par Delphi
à partir des informations que vous lui avez fourni. Comme les fichiers projet, ils
peuvent aussi être modifiés manuellement. Un fichier source de paquet devrait
être enregistré avec l’extension .DPK (pour Delphi PacKage) afin d’éviter toute
confusion avec les autres fichiers contenant du code source Pascal Objet.
Pour ouvrir un fichier source de paquet dans l’éditeur de code,
1 Ouvrez les paquets dans l’éditeur de paquets.
2 Cliquez avec le bouton droit de la souris dans l’éditeur de paquets et
sélectionnez Voir le source.
• L’en-tête package spécifie le nom du paquet.
• La clause requires énumère d’autres paquets externes utilisés par le paquet
en cours. Si un paquet ne contient pas d’unité qui utilise des unités d’un
autre paquet, il n’a pas besoin de clause requires.
• La clause contains identifie les fichiers unité à compiler et à rassembler
dans le paquet. Toutes les unités utilisées par des unités de la clause
contains qui ne se trouvent pas dans les paquets de la clause requires
seront aussi rassemblées dans le paquet, même si elle ne sont pas indiquées
dans la clause (le compilateur indique un avertissement).

Utilisation des paquets et des composants 9-9


Création et modification de paquets

Par exemple, le code suivant déclare le paquet VCLDB50.


package VCLDB50;
requires VCL50;
contains Db, Dbcgrids, Dbctrls, Dbgrids, Dbinpreq, Dblogdlg, Dbpwdlg, Dbtables,
mycomponent in ‘C:\components\mycomponent.pas’;
end.

Présentation de la structure d’un paquet


Nom de paquets
Les noms de paquets doivent être uniques dans un projet. Si un paquet a le nom
STATS, l’éditeur de paquet génère un fichier source correspondant appelé
STATS.DPK ; le compilateur génère un exécutable et une image binaire appelés
respectivement STATS.BPL et STATS.DCP. Utilisez STATS pour vous référer au
paquet dans la clause requires d’un autre paquet, ou lors de l’utilisation du
paquet dans une application.

La clause Requires
La clause requires spécifie les autres paquets, externes, utilisés par le paquet en
cours. Un paquet externe inclus dans la clause requires est automatiquement lié
lors de la compilation dans toute application utilisant le paquet en cours ou
l’une des unités contenues dans le paquet externe.
Si les fichiers unité contenus dans votre paquet font référence à d’autres unités
empaquetées, les autres paquets doivent apparaître dans la clause requires de
votre paquet, sinon vous devrez les ajouter. Si les autres paquets sont omis de la
clause requires, le compilateur les importera dans votre paquet comme ‘unités
contenues implicitement’.
Remarque La plupart des paquets nécessitent VCL50. Tout paquet dépendant des unités
VCL (y compris SysUtils) doit lister VCL50 ou un autre paquet nécessitant
VCL50 dans sa clause requires.

Pas de référence circulaire


Les paquets ne doivent pas contenir de référence circulaire dans leur clause
requires. Par conséquent :
• Un paquet ne doit pas se référencer lui-même dans sa clause requires.
• Une chaîne de références doit se terminer sans référencer un paquet de la
chaîne. Si le paquet A requiert le paquet B, alors le paquet B ne doit pas
requérir le paquet A ; si le paquet A requiert le paquet B qui requiert le
paquet C, alors le paquet C ne doit pas requérir le paquet A.

Gestion des références de paquet dupliquées


Les références en double dans la clause requires d'un paquet, ou dans la boîte
de saisie Paquet d'exécution, sont ignorées. Mais, pour la lisibilité du
programme, il vaut mieux les supprimer.

9-10 Guide du développeur


Création et modification de paquets

La clause Contains
La clause contains identifie les fichiers unité à lier dans le paquet. Si vous
écrivez votre propre paquet, placez votre code source dans des fichiers PAS et
incluez-les dans la clause contains.

Eviter l’utilisation de code source redondant


Un paquet ne peut apparaître dans la clause contains d’un autre paquet.
Toutes les unités incluses directement dans la clause contains d’un paquet, ou
indirectement dans l’une de ces unités sont liées dans le paquet au moment de la
compilation.
Une unité ne peut être contenue (directement ou indirectement) dans plusieurs
des paquets utilisés par une même application, y compris l’IDE Delphi. Cela
signifie que si vous créez un paquet contenant l’une des unités de VCL50, vous
ne pourrez pas installer ce paquet dans l’EDI. Pour utiliser une unité déjà
empaquetée dans un autre paquet, placez le premier paquet dans la clause
requires du second paquet.

Compilation de paquets
Vous pouvez compiler un paquet dans l’EDI ou depuis la ligne de commande.
Pour recompiler un paquet directement dans l’EDI :
1 Choisissez Fichier|Ouvrir.
2 Sélectionnez le fichier source du paquet Delphi (*.DPK) à partir de la liste
déroulante Fichiers de type.
3 Sélectionnez un fichier .DPK dans la boîte de dialogue.
4 Lorsque l’éditeur de paquet est ouvert, cliquez sur le turbobouton Compiler.
Vous pouvez insérer des directives de compilation dans le code source du paquet.
Pour plus d’informations, voir le paragraphe ci-dessous.
Si vous compilez à partir de la ligne de commande, de nouvelles options d’édition
de lien spécifiques aux paquets sont utilisables. Pour plus d’informations, voir
“Utilisation du compilateur et du lieur en ligne de commande” à la page 9-13.

Directives de compilation propres aux paquets


Le tableau suivant liste les directives de compilation propres aux paquets qu’il
est possible d’insérer dans le code source.

Tableau 9.4 Directives de compilation propres aux paquets


Directive Fonction
{$IMPLICITBUILD OFF} Empêche une recompilation implicite du paquet. Utilisez-
la dans les fichiers .DPK lors de la compilation de
paquets qui fournissent des fonctionnalités de bas niveau
et qui ne sont pas modifiées fréquemment ou dont le
source n’est pas distribué.

Utilisation des paquets et des composants 9-11


Création et modification de paquets

Tableau 9.4 Directives de compilation propres aux paquets (suite)


Directive Fonction
{$G-} ou {IMPORTEDDATA OFF} Désactive la création de références de données importées.
Cette directive augmente l’efficacité des accès mémoire,
mais empêche l’unité où elle se trouve de faire référence
à des variables d’une autre unité.
{$WEAKPACKAGEUNIT ON} Les unités sont “faiblement empaquetées”. Voir “Paquets
faibles” à la page 9-12 ci-dessous.
{$DENYPACKAGEUNIT ON} Empêche les unités d’être placées dans un paquet.
{$DESIGNONLY ON} Compile le paquet pour une installation dans l’EDI.
(Mettre dans le fichier .DPK.)
{$RUNONLY ON} Compile le paquet comme exécutable seulement. (Mettre
dans le fichier .DPK.)

Remarque Utilisez {$DENYPACKAGEUNIT ON} dans votre code pour que l’unité ne soit
pas mise dans un paquet. L’utilisation de {$G-} ou {IMPORTEDDATA OFF}
permet à un paquet de ne pas être utilisé dans la même application avec
d’autres paquets. Les paquets compilés avec la directive {$DESIGNONLY ON}
ne devrait pas être utilisés dans les applications puisque qu’ils contiennent du
code nécessaire à l’EDI. D’autres directives de compilation peuvent être utilisées
dans le code source de paquet. Voir Directives de compilation dans l’aide en
ligne pour les directives de compilation qui n’ont pas été abordées ici.

Paquets faibles
La directive $WEAKPACKAGEUNIT affecte la manière dont un fichier .DCU est
stocké dans les fichiers .DCP et .BPL d’un paquet. Pour des informations sur les
fichiers générés par le compilateur, voir “Fichiers paquets créés lors d’une
compilation réussie” à la page 9-13.) Si {$WEAKPACKAGEUNIT ON} apparaît
dans un fichier unité, le compilateur omet l’unité des BPL si c’est possible, et
crée une version locale non empaquetée de l’unité quand elle est nécessaire à
une autre application ou un autre paquet. Une unité compilée avec cette
directive est dite “faiblement empaquetée”.
Si, par exemple, vous créez un paquet appelé PACK ne contenant que l’unité
UNIT1. Supposez que UNIT1 n’utilise aucune autre unité, mais fait des appels à
RARE.DLL. Si vous placez la directive {$WEAKPACKAGEUNIT ON} dans
UNIT1.PAS, lors de la compilation du paquet, UNIT1 n’est pas incluse dans
PACK.BPL ; vous n’avez donc pas à distribuer de copie de RARE.DLL avec
PACK. Néanmoins, UNIT1 sera toujours incluse dans PACK.DCP. Si UNIT1 est
référencée dans un autre paquet ou une autre application utilisant PACK, elle
sera copiée dans PACK.DCP et directement compilée dans le projet.
Supposons maintenant que vous ajoutiez à PACK une deuxième unité, UNIT2
qui utilise UNIT1. Cette fois, même si vous compilez PACK avec la directive
{$WEAKPACKAGEUNIT ON} dans UNIT1.PAS, le compilateur inclut UNIT1
dans PACK.BPL. Par contre les autres paquets ou applications faisant référence à
UNIT1 utiliseront la copie (non empaquetée) prise dans PACK.DCP.
Remarque Les fichiers unité contenant la directive {$WEAKPACKAGEUNIT ON} ne
doivent pas contenir de variables globales, de section d’initialisation ou de
sections de finalisation.

9-12 Guide du développeur


Création et modification de paquets

La directive $WEAKPACKAGEUNIT est une caractéristique avancée proposée


pour les développeurs distribuant leurs BPL à d’autres programmeurs Delphi.
Cela permet d’éviter la distribution de DLL rarement utilisées ou d’éliminer des
conflits entre des paquets dépendant d’une même bibliothèque externe.
Ainsi, l’unité PenWin de C++Builder référence PENWIN.DLL. La plupart des
projets n’utilisent pas PenWin et la plupart des ordinateurs n’ont pas de fichier
PENWIN.DLL installé. C’est pour cela que l’unité PenWin est faiblement
empaquetée dans VCL50. Quand vous compilez un projet utilisant PenWin et le
paquet VCL50, PenWin est copié depuis VCL50.DCP et lié directement à votre
projet ; l’exécutable résultant est lié statiquement à PENWIN.DLL.
Si PenWin n’était pas faiblement empaqueté, il se poserait deux problèmes. Tout
d’abord, il faudrait que VCL50 soit lié de manière statique à PENWIN.DLL et ne
pourrait de ce fait être chargé que sur un système disposant de PENWIN.DLL
installé. De plus, si vous tentez de créer un paquet contenant PenWin, une erreur
de compilation aurait lieu, puisque l’unité PenWin serait contenue dans VCL50
et dans votre paquet. Ainsi, sans “empaquetage faible”, l’unité PenWin n’aurait
pas pu être incluse dans la distribution standard de VCL50.

Utilisation du compilateur et du lieur en ligne de commande


Quand vous compilez depuis la ligne de commande, utilisez les options
spécifiques aux paquets présentées dans le tableau suivant.

Tableau 9.5 Options du compilateur en ligne de commande propres aux paquets


Option Fonction
-$G- Désactive la création de références de données importées. L’utilisation de cette
option augmente l’efficacité des accès mémoire, mais empêche les paquets compilés
avec cette option de référencer des variables appartenant à d’autres paquets.
-LEpath Spécifie le répertoire où se trouvera le fichier BPL du paquet.
-LNpath Spécifie le répertoire où se trouvera le fichier DCP du paquet.
-LUpackage Utilise les paquets.
-Z Empêche la recompilation implicite ultérieure d’un paquet. Utilisez cette option
lors de la compilation de paquets qui fournissent des fonctionnalités de bas
niveau, qui changent peu souvent entre les builds, ou dont le code source ne
sera pas distribué.

Remarque L’utilisation de l’option -$G- empêche un paquet d’être utilisé dans une même
application avec d’autres paquets. Les autres options en ligne de commande
peuvent être utilisées de manière appropriée lors de la compilation des paquets.
Voir “Le compilateur en ligne de commande” dans l’aide en lignepour les
options en ligne de commande qui n’ont pas été abordées ici.

Fichiers paquets créés lors d’une compilation réussie


Pour créer un paquet, compilez un fichier source ayant l’extension .DPK. Le nom
de base du fichier source doit correspondre au nom de base des fichiers générés
par le compilateur ; c’est-à-dire que si le fichier source du paquet s’appelle
TRAYPAK.DPK, le compilateur crée un paquet appelé TRAYPAK.BPL.

Utilisation des paquets et des composants 9-13


Déploiement de paquets

Le tableau suivant donne la liste des fichiers générés par une compilation réussie
d’un paquet.

Tableau 9.6 Fichiers compilés d’un paquet


Extension
de fichier Contenu
DCP Une image binaire contenant un en-tête de paquet et la concaténation de
tous les fichiers DCU du paquet. Un seul fichier DCP est créé pour chaque
paquet. Le nom pour le fichier DCP est celui du fichier source DPK.
DCU Une image binaire pour un fichier unité contenu dans un paquet. Lorsque
cela est nécessaire, un seul fichier DCU est créé pour chaque fichier unité.
BPL Le paquet d’exécution. Ce fichier est une DLL avec des caractéristiques
spécifiques à Delphi. Le nom de base du BPL est celui du fichier source
DPK.

Déploiement de paquets
Déploiement d’applications utilisant des paquets
Pour distribuer une application utilisant des paquets d’exécution, vérifiez que
l’utilisateur dispose du fichier .EXE de l’application, ainsi que de toutes les
bibliothèques (.BPL ou .DLL) appelées par l’application. Si les fichiers
bibliothèque sont dans un répertoire différent de celui du fichier .EXE, ils
doivent être accessibles via les chemins d’accès de l’utilisateur. Vous pouvez
suivre la convention consistant à placer les fichiers des bibliothèques dans le
répertoire Windows\System. Si vous utilisez InstallShield Express, le script
d’installation peut vérifier la présence des paquets nécessaires sur le système de
l’utilisateur avant de les réinstaller aveuglément.

Distribution de paquets à d’autres développeurs


Si vous distribuez des paquets d’exécution ou de conception à d’autres
développeurs Delphi, assurez-vous de fournir les fichiers .DCP et .BPL. Vous
aurez probablement besoin d’inclure aussi les fichiers .DCU.

Fichiers de collection de paquet


Les collections de paquet (fichiers .DPC) offrent un moyen pratique de distribuer
des paquets à d’autres développeurs. Chaque collection de paquet contient un ou
plusieurs paquets, comprenant les BPL et les fichiers supplémentaires que vous
voulez distribuer avec. Lorsqu’une collection de paquet est sélectionnée pour une
installation de l’EDI, les fichiers qui la constituent sont automatiquement extraits
du fichier conteneur .PCE ; la boîte de dialogue Installation offre la possibilité
d’installer tous les paquets de la collection ou ceux sélectionnés.

9-14 Guide du développeur


Déploiement de paquets

Pour créer une collection de paquet,


1 Choisissez Outils|Editeur de collection de paquets pour ouvrir l’éditeur de
collection de paquets.
2 Cliquez sur le turbobouton Ajouter un paquet, sélectionnez un BPL dans la
boîte de dialogue Sélectionner le paquet et choisissez Ouvrir. Pour ajouter
d’autres BPL à la collection, choisissez à nouveau le turbobouton Ajouter un
paquet. Sur le côté gauche de l’éditeur de paquets, un diagramme
arborescence affiche les BPL que vous avez ajouté. Pour supprimer un paquet,
sélectionnez et choisissez le turbobouton Supprimer un paquet.
3 Sélectionnez le nœud Collection en haut du diagramme arborescence. Sur la
partie droite de l’éditeur de collection de paquets, deux champs apparaissent :
• Dans la boîte de saisie Nom auteur/vendeur, vous pouvez saisir des
informations optionnelles à propos de votre collection de paquets qui
apparaîtront dans la boîte de dialogue Installation lorsque les utilisateurs
installeront les paquets.
• Dans la liste Nom de répertoire, énumérez les répertoires dans lesquels
vous voulez installer les fichiers de la collection de paquets. Utilisez les
boutons Ajouter, Edition et Supprimer pour modifier cette liste. Par
exemple, supposons que vous voulez copier tous les fichiers de code source
dans un même répertoire. Dans ce cas, vous pouvez saisir comme Source le
nom de répertoire C:\MyPackage\Source, comme le chemin suggéré. La boîte
de dialogue Installation affichera C:\C:\MyPackage\Source comme chemin
suggéré pour le répertoire.
4 En plus des BPL, la collection de paquets peut contenir des fichiers .DCP,
.DCU, et des unités .PAS, de la documentation, et d’autres fichiers que l’on
peut inclure pour la distribution. Les fichiers annexes sont placés dans des
groupes de fichiers associés aux paquets spécifiques (BPL). Les fichiers d’un
groupe ne sont installés que lorsque le BPL associé est installé. Pour mettre les
fichiers annexes dans la collection de paquets, sélectionnez un BPL dans le
diagramme arborescence et choisissez le turbobouton Ajouter un groupe de
fichiers ; saisissez un nom pour le groupe de fichiers. Ajoutez d’autres fichiers
si vous le désirez en procédant de la même manière. Lorsque vous
sélectionnez un groupe de fichiers, de nouveaux champs apparaissent sur la
droite de l’éditeur de collection de paquets.
• Dans la boîte liste Répertoire d’installation, sélectionnez le répertoire dans
lequel vous voulez installer les fichiers de ce groupe. La liste déroulante
comprend les répertoires saisis dans liste de répertoires de l’étape 3
ci-dessus.
• Vérifiez la case à cocher Groupe optionnel si vous voulez que l’installation
des fichiers de ce groupe soit facultative.
• Sous Fichiers inclus, énumérez les fichiers que vous voulez inclure dans ce
groupe. Utilisez les boutons Ajouter, Supprimer et Auto pour modifier la
liste. Le bouton Auto permet de sélectionner tous les fichiers avec
l’extension spécifiée dont la liste se trouve dans la clause contains du
paquet ; l’éditeur de collection de paquets utilise le chemin de la
bibliothèque de Delphi pour rechercher ces fichiers.

Utilisation des paquets et des composants 9-15


Déploiement de paquets

5 Vous pouvez sélectionner des répertoires d’installation pour les paquets dont
la liste se trouve dans la clause requires de n’importe quel paquet de votre
collection. Lorsque vous sélectionnez un BPL dans la liste arborescente, quatre
nouveaux champs apparaissent sur la partie droite de l’éditeur de collection
de paquets :
• Dans la boîte liste Fichiers exécutables requis, sélectionnez le répertoire
dans lequel vous voulez installer les fichiers .BPL pour les paquets dont la
liste se trouve dans la clause requires. La liste déroulante comprend les
répertoires saisis dans Nom de répertoire à l’étape 3 ci-dessus. L’éditeur de
collection de paquets recherche ces fichiers en utilisant le chemin de la
bibliothèque Delphi et donne la liste sous Fichiers exécutables requis.
• Dans la boîte liste Répertoire des bibliothèques requises, sélectionnez le
répertoire d’installation des fichiers.DCP pour les paquets de la clause
requires. La liste déroulante comprend les répertoires spécifiés sous Nom
répertoire à l’étape 3, ci-dessus. L’éditeur de collection de paquets recherche
ces fichiers en utilisant le chemin de bibliothèque global de Delphi, et les
affiche sous Fichiers bibliothèque requis.
6 Pour enregistrer votre fichier source de collection de paquets, choisissez
Fichier|Enregistrer. Les fichiers source de collection de paquets doivent être
enregistrés avec l’extension .PCE.
7 Pour construire votre collection de paquets, choisissez le turbobouton
Compiler. L’éditeur de collection de paquets génère un fichier .DPC avec le
nom de votre fichier source (.PCE). Si vous n’avez pas encore enregistré le
fichier source, l’éditeur vous demande un nom de fichier avant la compilation.
Pour éditer ou recompiler un fichier .PCE existant, sélectionnez Fichier|Ouvrir
dans l’éditeur de collection de paquets.

9-16 Guide du développeur


Chapitre

Création d’applications
Chapter 10
10
internationales
Ce chapitre présente les règles d’écriture d’applications qui seront distribuées sur
les marchés internationaux. En planifiant le processus, il est possible de réduire
le temps et le code nécessaires pour que vos applications puissent fonctionner
parfaitement à l’étranger comme sur le marché domestique.

Internationalisation et localisation
Pour créer une application distribuable sur les marchés étrangers, vous devez
accomplir deux étapes :
• Internationalisation
• Localisation

Internationalisation
L’internationalisation est le processus permettant à votre application de
fonctionner selon divers paramètres régionaux. Les paramètres régionaux sont
l’environnement de l’utilisateur qui inclut les conventions culturelles du pays
cible aussi bien que sa langue. Windows gère un grand nombre de paramètres
régionaux, chacun d’eux décrits par l’association d’une langue et d’un pays.

Localisation
La localisation est le processus de traduction d’une application pour qu’elle
fonctionne pour des paramètres régionaux spécifiques. Outre la traduction de
l’interface utilisateur, la localisation peut également consister à personnaliser les
fonctionnalités. Par exemple, une application financière peut être modifiée afin
de respecter les règles fiscales dans différents pays.

Création d’applications internationales 10-1


Internationalisation des applications

Internationalisation des applications


Il n’est pas difficile de créer des applications internationalisées. Il suffit
d’effectuer les étapes suivantes :
1 Le code de votre application doit être capable de gérer des jeux de caractères
internationaux.
2 Vous devez concevoir l’interface utilisateur de l’application afin de l’adapter
aux modifications résultant de la localisation.
3 Vous devez isoler toutes les ressources qui ont besoin d’être localisées.

Codage de l’application
Vous vous assurerez que le code de l’application peut gérer les chaînes qu’elle
rencontrera dans les divers environnements régionaux cible.

Jeux de caractères
Les versions de Windows diffusées aux USA utilisent le jeu de caractères ANSI
Latin-1 (1252). Mais d’autres éditions de Windows utilisent des jeux de caractères
différents. Ainsi, la version japonaise de Windows utilise le jeu de caractères
Shift-Jis (page de code 932) qui représente les caractères japonais avec des codes
sur un ou deux octets.

Jeux de caractères OEM et ANSI


Il est parfois nécessaire de faire des conversions entre le jeu de caractères
Windows (ANSI) et le jeu de caractères spécifié par la page de code de la
machine de l’utilisateur (appelé jeu de caractères OEM).

Jeux de caractères sur deux octets


Les idéogrammes utilisés en Asie ne peuvent se satisfaire de la correspondance
1 pour 1 existant entre les caractères d’une langue et le type char qui occupe un
seul octet (8 bits). Ces langues ont trop de caractères pour qu’ils soient
représentés sur un seul octet comme le fait le type char. Les idéogrammes sont
représentés par un mélange de codes sur un et deux octets.
Le premier octet de tous les codes utilisant deux octets appartient à un intervalle
réservé et spécifique du jeu de caractères concerné. Le second octet peut être le
code d’un autre caractère s’il s’agit d’un caractère codé sur un seul octet, ou il
peut appartenir à l’intervalle réservé indiqué par le premier octet s’il s’agit d’un
caractère codé sur deux octets. Aussi, la seule façon de savoir si un octet
particulier dans une chaîne représente seul un caractère ou fait partie d’un
groupe de deux octets est de lire la chaîne à partir de l’origine, en la
décomposant en caractères de deux octets chaque fois qu’on a rencontré un octet
appartenant à l’intervalle réservé.

10-2 Guide du développeur


Internationalisation des applications

Lors de l’écriture de code destiné aux pays asiatiques, vous devez traiter toutes
les manipulations de chaînes avec des fonctions capables de décomposer les
chaînes en caractères de un ou deux octets. Delphi fournit un certain nombre de
fonctions de la bibliothèque d’exécution pour effectuer cela. Ces fonctions sont
les suivantes :

AdjustLineBreaks AnsiStrLower ExtractFileDir


AnsiCompareFileName AnsiStrPos ExtractFileExt
AnsiExtractQuotedStr AnsiStrRScan ExtractFileName
AnsiLastChar AnsiStrScan ExtractFilePath
AnsiLowerCase AnsiStrUpper ExtractRelativePath
AnsiLowerCaseFileName AnsiUpperCase FileSearch
AnsiPos AnsiUpperCaseFileName IsDelimiter
AnsiQuotedStr ByteToCharIndex IsPathDelimiter
AnsiStrComp ByteToCharLen LastDelimiter
AnsiStrIComp ByteType StrByteType
AnsiStrLastChar ChangeFileExt StringReplace
AnsiStrLComp CharToByteIndex WrapText
AnsiStrLIComp CharToByteLen

N’oubliez pas que la longueur de la chaîne en octets ne correspond pas


nécessairement à la longueur de la chaîne en caractères. Faites attention à ne pas
tronquer les chaînes en coupant en deux un caractère codé sur deux octets. Vous
ne pouvez pas passer des caractères comme paramètres aux fonctions ou aux
procédures puisque la taille d’un caractère n’est pas connue directement. Vous
devez passer un pointeur sur le caractère ou sur la chaîne.

Caractères larges
Une autre approche des idéogrammes est de convertir tous les caractères dans
un système de caractères larges, comme Unicode. Les caractères larges utilisent
deux octets au lieu d’un, si bien qu’un jeu de ces caractères peut contenir un
nombre beaucoup plus grand d’éléments.
En outre, les caractères larges présentent un avantage sur les caractères MBCS :
ils vous permettent de conserver vos habitudes car il existe une relation directe
entre le nombre d’octets d’une chaîne et son nombre de caractères. Et, vous ne
risquez plus de couper un caractère en deux, ni de confondre la seconde moitié
d’un caractère avec la première d’un autre.
L’inconvénient majeur des caractères larges est que Windows 95 n’en reconnaît
qu’un petit nombre dans les appels aux fonctions API. C’est pourquoi, les
composants VCL représentent toutes les valeurs chaînes par des chaînes à
caractères d’un seul octet ou par des chaînes MBCS. Vous devrez passer du

Création d’applications internationales 10-3


Internationalisation des applications

système caractères larges au système MBCS à chaque fois que définir la propriété
d’une chaîne ou en lire la valeur exigerait une grande quantité de code et
ralentirait votre application. Cependant, vous pouvez souhaiter traduire en
caractères larges certains algorithmes de traitement des chaînes pour profiter de
la correspondance 1 pour 1 entre caractères et WideChars.

Inclure des fonctionnalités bi-directionnelles dans les


applications
Certaines langues ne se lisent pas de gauche à droite comme la plupart des
langues occidentales, mais elles lisent les mots de droite à gauche et comptent de
gauche à droite. Ces langues sont dites bi-directionnelles (BiDi) du fait de cette
séparation. Les langues bi-directionnelles les plus courantes sont l’Arabe et
l’Hébreux, sans parler d’autres langues de l’Est.
TApplication dispose de deux propriétés, BiDiKeyboard et NonBiDiKeyboard, vous
permettant de spécifier la disposition clavier. En outre, la VCL gère la
localisation bi-directionnelle via les propriétés BiDiMode et ParentBiDiMode. Le
tableau suivant énumère les objets de la VCL possédant ces propriétés :

Tableau 10.1 Objets de la VCL supportant les BiDi


Page de la palette
des composants Objet de la VCL
Standard TButton
TCheckBox
TComboBox
TEdit
TGroupBox
TLabel
TListBox
TMainMenu
TMemo
TPanel
TPopupMenu
TRadioButton
TRadioGroup
TScrollBar
Supplément TBitBtn
TCheckListBox
TDrawGrid
TMaskEdit
TScrollBox
TSpeedButton
TStaticLabel
TStringGrid

10-4 Guide du développeur


Internationalisation des applications

Tableau 10.1 Objets de la VCL supportant les BiDi (suite)


Page de la palette
des composants Objet de la VCL
Win32 TDateTimePicker
THeaderControl
TListView
TMonthCalendar
TPageControl
TRichEdit
TStatusBar
TTabControl
Contrôles de données TDBCheckBox
TDBComboBox
TDBEdit
TDBGrid
TDBListBox
TDBLookupComboBox
TDBLookupListBox
TDBMemo
TDBRadioGroup
TDBRichEdit
TDBText
QReport TQRDBText
TQRExpr
TQRLabel
TQRMemo
TQRSysData
Autres classes TApplication (sans ParentBiDiMode)
TForm
THintWindow (sans ParentBiDiMode)
TStatusPanel
THeaderSection

Remarque THintWindow capte la valeur de BiDiMode du contrôle qui a activé le conseil.

Propriétés bi-directionnelles
Les objets dont la liste est donnée dans le tableau 10.1, “Objets de la VCL
supportant les BiDi,” à la page 10-4 ont les propriétés : BiDiMode et
ParentBiDiMode. Ces propriétés, ainsi que BiDiKeyboard et NonBiDiKeyboard de
TApplication gèrent la localisation bi-directionnelle.

Création d’applications internationales 10-5


Internationalisation des applications

Propriété BiDiMode
La propriété BiDiMode est un nouveau type d’énuméré, TBiDiMode, qui possède
quatre états : bdLeftToRight, bdRightToLeft, bdRightToLeftNoAlign, et
bdRightToLeftReadingOnly.

bdLeftToRight
bdLeftToRight dessine le texte en utilisant le sens de lecture de gauche à droite,
l’alignement et la barre de défilement étant inchangés. Par exemple, lors de la
saisie de texte de droite à gauche, comme pour l’Arabe ou l’Hébreux, le curseur
passe en mode poussoir et le texte est saisi de droite à gauche. Pour du texte
latin, comme l’Anglais ou le Français, il est saisi de gauche à droite.
bdLeftToRight est la valeur par défaut.
Figure 10.1 Contrôles initialisés à bdLeftToRight

bdRightToLeft
bdRightToLeft dessine le texte en utilisant le sens de lecture de droite à gauche,
l’alignement étant modifié et la barre de défilement déplacée. Le texte est saisi
normalement pour les langues allant de droite à gauche comme l’Arabe ou
l’Hébreux. Lorsque le clavier est modifié pour une langue latine, le curseur passe
en mode poussoir et le texte est saisi de gauche à droite.
Figure 10.2 Contrôles initialisés à bdRightToLeft

bdRightToLeftNoAlign
bdRightToLeftNoAlign dessine le texte en utilisant le sens de lecture de droite à
gauche, l’alignement étant inchangé et la barre de défilement déplacée.
Figure 10.3 Contrôles initialisés à bdRightToLeftNoAlign

bdRightToLeftReadingOnly
bdRightToLeftReadingOnly dessine le texte en utilisant le sens de lecture de droite
à gauche, l’alignement et la barre de défilement étant inchangés.
Figure 10.4 Contrôles initialisés à bdRightToLeftReadingOnly

10-6 Guide du développeur


Internationalisation des applications

Propriété ParentBiDiMode
ParentBiDiMode est une propriété booléenne. Lorsqu’elle est à True (la valeur par
défaut), le contrôle regarde la propriété de son parent pour connaître la valeur à
utiliser pour BiDiMode. Si le contrôle est un objet TForm, la fiche utilise la valeur
BiDiMode de Application. Si toutes les propriétés ParentBiDiMode sont à True,
lorsque la propriété BiDiMode de Application est modifiée, toutes les fiches et tous
les contrôles du projet sont initialisés avec la nouvelle valeur.

Méthode FlipChildren
La méthode FlipChildren vous permet de faire basculer la position des enfants
d’un contrôle conteneur. Les contrôles conteneur sont des contrôles qui
contiennent d’autres contrôles, comme TForm, TPanel et TGroupbox. FlipChildren
possède un seul paramètre booléen, AllLevels. Lorsqu’il est à False, seuls les
enfants directs du contrôle conteneur sont basculés de position. Lorsqu’il est à
True, tous les enfants du contrôle conteneur sont basculés de position.
Delphi fait basculer la position des contrôles en modifiant la propriété Left et
l’alignement du contrôle. Si le côté gauche d’un contrôle est à cinq pixels de la limite
gauche de son parent, le basculement provoque l’affichage du côté droit du contrôle de
saisie à cinq pixels de la limite droite de son parent. Si le contrôle de saisie est aligné à
gauche, un appel à FlipChildren provoquera un alignement à droite.
Pour basculer la position d’un contrôle lors de la conception, il faut sélectionner
Edition|Transposer les enfants et sélectionner Tous ou Sélectionnés suivant que
vous voulez basculer la position de tous les contrôles ou seulement les enfants
du contrôle sélectionné. Il est aussi possible de basculer la position d’un contrôle
en sélectionnant le contrôle sur la fiche, en cliquant sur le bouton droit de la
souris pour sélectionner le choix Transposer les enfants dans le menu contextuel.
Remarque La sélection d’un contrôle de saisie suivi de la commande Transposer les
enfants|Sélectionnés ne fait rien. Cela est du au fait que les contrôles de saisie
ne sont pas des conteneurs.

Autres méthodes
Il existe d’autres méthodes utiles afin de développer des applications pour des
utilisateurs bi-directionnels.

Méthode Description
OkToChangeFieldAlignment Utilisée avec les contrôles base de données. Vérifie si
l’alignement d’un contrôle peut être modifié.
DBUseRightToLeftAlignment Utilisée pour vérifier l’alignement des contrôles base
de données.
ChangeBiDiModeAlignment Modifie le paramètre d’alignement qui lui est
transmis. Aucune vérification n’est faite pour
l’initialisation de BiDiMode, car il y a juste
conversion de l’alignement à gauche vers
l’alignement à droite et vice-versa, en laissant centré
les contrôles seuls.

Création d’applications internationales 10-7


Internationalisation des applications

Méthode Description
IsRightToLeft Renvoie True si une des options allant de droite à
gauche est sélectionnée. Renvoie False si le contrôle
est dans un mode allant de gauche à droite.
UseRightToLeftReading Renvoie True si le contrôle utilise le sens de lecture
allant de droite à gauche.
UseRightToLeftAlignment Renvoie True si le contrôle utilise le sens
d’alignement allant de droite à gauche. Il peut être
surchagé pour être personnalisé.
UseRightToLeftScrollBar Renvoie True si le contrôle utilise une barre de
défilement à gauche.
DrawTextBiDiModeFlags Renvoie les bons paramètres pour le mode BiDi du
contrôle.
DrawTextBiDiModeFlagsReadingOnly Renvoie les bons paramètres pour le mode BiDi du
contrôle, en les limitant à la lecture.
AddBiDiModeExStyle Ajoute le paramètre ExStyle flags approprié au
contrôle créé.

Fonctionnalités spécifiques aux cibles locales


Vous pouvez ajouter à votre application des fonctionnalités supplémentaires pour
des cibles locales spécifiques. En particulier, pour les langues asiatiques, il peut être
nécessaire à votre application de contrôler l’IME (Input Method Editor) utilisé pour
convertir en chaînes de caractères les touches frappées au clavier par l’utilisateur.
Les composants VCL supportent la programmation de l’IME. La plupart des
contrôles fenêtrés autorisant directement la saisie de texte possèdent une
propriété ImeName qui permet de spécifier l’IME à utiliser lorsque le contrôle
reçoit la saisie. Ces contrôles possèdent également une propriété ImeMode qui
permet de spécifier en quoi l’IME doit convertir ce qui est frappé au clavier.
TWinControl introduit plusieurs méthodes protégées que vous pouvez utiliser
pour contrôler l’IME depuis les classes que vous avez définies. De plus, la
variable globale Screen vous fournit des informations concernant les IME
disponibles sur le système de l’utilisateur.
La variable globale Screen fournit également des informations concernant
l’affectation des touches utilisée sur le système de l’utilisateur. Vous pouvez
l’utiliser pour obtenir des informations sur les paramètres régionaux de
l’environnement dans lequel tourne votre application.

Conception de l’interface utilisateur


Lorsque vous créez une application pour plusieurs marchés étrangers, il est
important de concevoir son interface utilisateur afin qu’elle s’adapte aux
modifications effectuées lors de sa traduction.

10-8 Guide du développeur


Internationalisation des applications

Texte
Tout le texte apparaissant dans l’interface utilisateur doit être traduit. Le texte
anglais étant presque toujours plus court que les traductions, vous devez
concevoir les éléments de votre interface utilisateur qui affiche du texte en
réservant de l’espace pour l’expansion de ce texte. Concevez également les boîtes
de dialogue, les menus, les barres d’état et les autres éléments de l’interface
utilisateur affichant du texte de telle sorte qu’ils puissent facilement afficher des
chaînes plus longues. Evitez les abréviations qui ne peuvent exister dans les
langues utilisant des idéogrammes.
Les chaînes courtes grandissent plus que les phrases longues. Le tableau suivant
fournit une approximation des taux de foisonnement selon la longueur de la
chaîne initiale (en anglais) :
Tableau 10.2 Estimation des longueurs de chaîne
Longueur de la chaîne anglaise (en
caractères) Augmentation prévisible
1-5 100%
6-12 80%
13-20 60%
21-30 40%
31-50 20%
over 50 10%

Images graphiques
Le mieux est d’utiliser des images qui ne nécessitent pas de traduction, c’est-à-
dire des images qui ne contiennent pas de texte. Si vous devez inclure du texte
dans vos images, il est préférable d’utiliser un objet libellé avec arrière-plan
transparent par dessus l’image, plutôt que d’inclure le texte dans l’image elle-
même.
Voici quelques autres considérations à prendre en compte lors de la création des
images graphiques. Essayez d’éviter les images spécifiques à une culture. Par
exemple, les boîtes à lettres sont très différentes selon les pays. Les symboles
religieux ne conviennent pas aux pays où il existe plusieurs religions
dominantes. Même les couleurs ont des connotations symboliques différentes
selon les cultures.

Formats et ordre de tri


Les formats de date, formats horaires, numériques et monétaires utilisés dans
votre application doivent être localisés selon les paramètres régionaux. Si vous
utilisez uniquement les formats de Windows, vous n’avez rien à traduire puisque
Windows les lit dans la base de registres de l’utilisateur. Cependant, si vous
spécifiez vos propres chaînes de format, déclarez-les comme constantes de
ressource afin de pouvoir les localiser.

Création d’applications internationales 10-9


Internationalisation des applications

L’ordre dans lequel les chaînes sont classées dépend également du pays. De
nombreuses langues européennes utilisent des caractères accentués et sont
classées différemment selon les paramètres régionaux. En outre, certaines
combinaisons de deux caractères peuvent être traitées par le tri comme un seul
caractère. Par exemple, en espagnol, la combinaison ch est triée comme étant un
caractère unique compris entre le c et le d. Parfois, un caractère est trié comme
s’il s’agissait de deux caractères séparés, par exemple le eszett allemand.

Correspondances entre claviers


Faites attention aux combinaisons de touches utilisées comme raccourcis. Les
caractères disponibles sur le clavier américain ne sont pas tous accessibles
facilement sur les autres claviers. Lorsque cela est possible, utilisez les touches
numériques et les touches de fonction comme raccourcis, puisqu’elles sont
aisément accessibles sur tous les claviers.

Isolement des ressources


La partie la plus évidente de la localisation d’une application consiste à traduire
les chaînes apparaissant dans l’interface utilisateur. Pour créer une application
pouvant être traduite sans modifier le moindre code, les chaînes de l’interface
utilisateur doivent être toutes placées dans un seul module. Delphi crée
automatiquement un fichier .DFM contenant les ressources des menus, boîtes de
dialogue et des bitmaps.
Outre les éléments d’interface apparents, vous devez isoler toutes les chaînes,
comme les messages d’erreur proposés à l’utilisateur. Les ressources chaîne ne
sont pas incluses dans le fichier .DFM. Vous pouvez les isoler en déclarant des
constantes au moyen du mot clé resourcestring. Pour plus d’informations sur les
constantes de chaîne de ressource, voir le guide du langage Pascal Objet. Il vaut
mieux inclure toutes les chaînes de ressource dans une seule unité séparée.

Création de DLL de ressources


L’isolement des ressources simplifie le processus de traduction. Le niveau
suivant d’isolement des ressources consiste à créer un module DLL. Un module
.DLL contient toutes les ressources et uniquement les ressources d’un
programme. Les DLL de ressource permettent de créer un programme gérant
plusieurs localisations en changeant simplement de DLL de ressource.
Utilisez l’expert Ressource DLL pour créer un module de ressource pour une
application. Vous devez avoir ouvert un projet compilé et enregistré pour utiliser
l’expert module de ressource. Cela crée un fichier RC contenant les tables de
chaîne à partir des fichiers RC utilisés et des chaînes resourcestring du projet, et
génère un projet pour une DLL de ressource qui contient les fiches et le fichier
RES créé. Le fichier RES est compilé à partir du nouveau fichier RC.

10-10 Guide du développeur


Internationalisation des applications

Vous devez créer un module de ressource pour chaque traduction que vous
voulez gérer. Chaque module de ressource doit avoir une extension du nom de
fichier spécifique à la localisation cible. Les deux premiers caractères indiquent la
langue cible et le troisième le pays pour la localisation. Si vous utilisez l’expert
Ressource DLL, cela est géré pour vous. Sinon, utilisez le code suivant pour
obtenir le code local de la traduction cible :
unit locales;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
LocaleList: TListBox;
procedure Button1Click(Sender: TObject);
private
{ déclarations privées }
public
{ déclarations publiques }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
function GetLocaleData(ID: LCID; Flag: DWORD): string;
var
BufSize: Integer;
begin
BufSize := GetLocaleInfo(ID, Flag, nil, 0);
SetLength(Result, BufSize);
GetLocaleinfo(ID, Flag, PChar(Result), BufSize);
SetLength(Result, BufSize - 1);
end;
{ Appelé pour chaque localisation supportée. }
function LocalesCallback(Name: PChar): Bool; stdcall ;
var
LCID: Integer;
begin
LCID := StrToInt('$' + Copy(Name, 5, 4));
Form1.LocaleList.Items.Add(GetLocaleData(LCID, LOCALE_SLANGUAGE));
Result := Bool(1);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
EnumSystemLocales(@LocalesCallback, LCID_SUPPORTED);
end;
end.

Création d’applications internationales 10-11


Internationalisation des applications

Utilisation des DLL de ressource


L’exécutable, les DLL et les paquets constituant l’application contiennent toutes
les ressources nécessaires. Cependant, pour remplacer ces ressources par leurs
versions localisées, il suffit simplement de fournir à l’application les DLL de
ressource localisées portant le même nom que les fichiers EXE, DLL ou BPL.
Lorsque votre application démarre, elle vérifie les paramètres régionaux du
système. Si elle trouve des DLL de ressource ayant les mêmes noms que les
fichiers EXE, DLL ou BPL qu’elle utilise, elle examine l’extension de ces DLL. Si
l’extension d’un module ressource correspond à la langue et au pays des
paramètres régionaux du système, votre application utilise les ressources de ce
module plutôt que les ressources de l’exécutable, de la DLL ou du paquet. S’il n’y
a pas de module ressource correspondant à la fois à la langue et au pays, votre
application essaie de trouver un module ressource correspondant à la langue
seule. S’il n’y a pas de module ressource correspondant à la langue, votre
application utilise les ressources compilées avec l’exécutable, la DLL ou le paquet.
Si vous voulez que votre application utilise un module de ressource différent de
celui correspondant aux paramètres régionaux du système sur lequel elle
s’exécute, vous pouvez redéfinir l’entrée spécifiant la localisation dans les
registres Windows. Sous l’entrée HKEY_CURRENT_USER\Software\Borland\
Locales, ajoutez le chemin d’accès de l’application et le nom de fichier sous la
forme d’une chaîne et définissez comme valeur de la donnée l’extension du DLL
de ressource. Au démarrage, l’application recherche les DLL de ressource portant
cette extension avant de rechercher la localisation du système. L’affectation de
cette entrée de registre permet de tester des versions localisées de l’application
sans modifier la localisation de votre système.
Par exemple, la procédure suivante peut être utilisée dans le programme
d’installation ou de configuration afin de définir la localisation à utiliser au
moment de charger des applications Delphi :
procedure SetLocalOverrides(FileName: string, LocaleOverride: string);
var
Reg: TRegistry;
begin
Reg := TRegistry.Create;
try
if Reg.OpenKey(‘Software\Borland\Locales’, True) then
Reg.WriteString(LocalOverride, FileName);
finally
Reg.Free;
end;
Dans votre application, utilisez la fonction globale FindResourceHInstance pour
obtenir le handle du module de ressource en cours. Par exemple :
LoadStr(FindResourceHInstance(HInstance), IDS_AmountDueName, szQuery, SizeOf(szQuery));
Vous pouvez ainsi distribuer une seule application qui s’adapte automatiquement
à la localisation du système sur laquelle elle s’exécute en fournissant simplement
les DLL de ressource.

10-12 Guide du développeur


Localisation des applications

Basculement dynamique de DLL de ressource


En plus de la localisation d’une DLL de ressource au démarrage de l’application,
il est possible de basculer de DLL de ressource dynamiquement lors de
l’exécution. Pour ajouter cette fonctionnalité à vos propres applications, vous
devez inclure l’unité ReInit dans votre instruction uses. ReInit se trouve dans
l’exemple Richedit du répertoire Demos. Pour basculer de langue, vous devez
appeler LoadResourceModule, en passant le LCID du nouveau langage, puis
appeler ReinitializeForms.
Par exemple, le code suivant bascule la langue en Français :
const
FRENCH = (SUBLANG_FRENCH shl 10) or LANG_FRENCH;
if LoadNewResourceModule(FRENCH) <> 0 then
ReinitializeForms;
L’avantage de cette technique est que l’instance en cours de l’application et de
toutes ses fiches est utilisée. Il n’est pas nécessaire de mettre à jour les
paramètres du registre et de redémarrer l’application ou de recharger les
ressources nécessaires à l’application, comme la connexion aux serveurs base de
données.
Lorsqu’il y a basculement de la DLL de ressource, les propriétés spécifiées dans
la nouvelle DLL écrasent celles des instances en cours d’exécution des fiches.
Remarque Toute modification effectuée dans les propriétés d’une fiche lors de l’exécution
est perdue. Une fois que la nouvelle DLL est chargée, les valeurs par défaut ne
sont pas initialisées. Evitez le code qui réinitialise les objets fiche dans leur état
de démarrage, mise à part les différences dues à la localisation.

Localisation des applications


Lorsque votre application est internationalisée, vous devez créer les versions
localisées pour les différents marchés étrangers sur lesquels vous souhaitez la
distribuer.

Localisation des ressources


Idéalement, vos ressources ont été isolées dans une DLL de ressource qui
contient les fichiers DFM et un fichier RES. Vous pouvez ouvrir vos fiches dans
l’EDI et traduire les propriétés importantes.
Remarque Dans un projet DLL de ressource, vous ne pouvez pas ajouter ou supprimer de
composant. Pourtant, il est possible de modifier les propriétés, au risque de
générer des erreurs d’exécution. Veillez donc à ne modifier que les propriétés
qui requièrent une traduction. Pour éviter les erreurs, vous pouvez configurer
l’inspecteur d’objets pour n’afficher que les propriétés localisables ; pour ce faire,
cliquez avec le bouton droit sur l’inspecteur d’objets et utilisez le menu Voir
pour filtrer les catégories de propriétés non souhaitées.

Création d’applications internationales 10-13


Localisation des applications

Vous pouvez ouvrir le fichier RC et traduire des chaînes appropriées. Utilisez


l’éditeur StringTable en ouvrant le fichier RC à partir du gestionnaire de projet.
Si votre version de Delphi inclut l’environnement de traduction intégré (ITE), vous
pouvez utiliser cet environnement pour gérer la localisation. Pour plus
d’informations, voir l’aide en ligne ITE.

10-14 Guide du développeur


Chapitre

Déploiement des applications


Chapter 11
11
Une fois votre application Delphi réalisée, vous devez la déployer, c’est-à-dire
permettre à d’autres de l’exécuter. Il est nécessaire d’effectuer un certain nombre
d’opérations pour déployer une application sur un autre ordinateur afin que
l’application soit entièrement opérationnelle. Les étapes nécessaires pour une
application donnée varient suivant le type de l’application. Les sections suivantes
décrivent les étapes du déploiement des applications :
• Déploiement d’applications généralistes
• Déploiement d’applications de base de données
• Déploiement d’applications Web
• Programmer pour des environnements hôtes hétérogènes
• Termes du contrat de licence logicielle

Déploiement d’applications généralistes


En dehors du fichier exécutable, une application peut nécessiter des fichiers
complémentaires, par exemple des DLL, des fichiers paquets ou des applications
complémentaires. De plus, l’application peut nécessiter des entrées dans les
registres Windows que ce soit pour spécifier l’emplacement de fichiers auxiliaires
ou le paramétrage de l’application. Il est possible d’automatiser avec un
programme d’installation, comme InstallShield Express, le processus de copie des
fichiers d’une application sur un ordinateur, ainsi que le paramétrage des entrées
de registre. Les étapes suivantes sont les principales étapes d’un déploiement et
concernent quasiment tous les types d’application :
• Utilisation des programmes d’installation
• Identification des fichiers de l’application

Déploiement des applications 11-1


Déploiement d’applications généralistes

Les applications Delphi qui accèdent à des bases de données ou qui fonctionnent
sur le Web nécessitent des étapes complémentaires d’installation en plus de
celles s’appliquant aux applications générales. Pour davantage d’informations sur
l’installation d’applications de base de données, voir “Déploiement d’applications
de base de données” à la page 11-4. Pour davantage d’informations sur
l’installation d’applications Web, voir “Déploiement d’applications Web” à la
page 11-7. Pour davantage d’informations sur l’installation de contrôles ActiveX,
voir “Déploiement d’un contrôle ActiveX sur le Web” à la page 48-19. Pour des
informations sur le déploiement d’applications CORBA, voir “Déploiement
d’applications CORBA” à la page 28-18.

Utilisation des programmes d’installation


Les applications Delphi simples, constituées d’un seul fichier exécutable,
s’installent facilement sur un ordinateur cible. Il suffit de copier le fichier sur
l’ordinateur. Mais les applications plus complexes composées de plusieurs
fichiers exigent une procédure d’installation plus sophistiquée. De telles
applications nécessitent un programme d’installation spécifique.
Les boîtes à outils d’installation automatisent le processus de création d’un
programme d’installation, le plus souvent sans avoir besoin d’écrire une seule
ligne de code. Les programmes d’installation créés par les boîtes à outils
d’installation effectuent diverses tâches liées à l’installation des applications
Delphi, y compris la copie de l’exécutable et des fichiers nécessaires sur
l’ordinateur cible, la création des entrées de registre Windows et l’installation du
moteur de bases de données Borland pour les applications de base de données.
InstallShield Express est une boîte à outils d’installation fournie avec Delphi.
InstallShield Express est spécialement adapté à l’utilisation de Delphi et du
moteur de bases de données Borland. InstallShield Express n’est pas installé
automatiquement lors de l’installation de Delphi, et doit être installé
manuellement afin de pouvoir créer des programmes d’installation. Exécutez le
programme d’installation du CD Delphi pour installer InstallShield Express. Pour
davantage d’informations sur l’utilisation de InstallShield Express, voir son aide
en ligne.
D’autres boîtes à outils d’installation sont disponibles, cependant vous ne devez
utiliser que celles certifiées pour déployer le moteur de bases de données
Borland (BDE).

Identification des fichiers de l’application


En plus du fichier exécutable, il peut être nécessaire de distribuer de nombreux
autres fichiers avec une application.
• Les fichiers de l’application, par extension de fichier
• Fichiers paquet
• Contrôles ActiveX

11-2 Guide du développeur


Déploiement d’applications généralistes

Les fichiers de l’application, par extension de fichier


Il peut être nécessaire de distribuer les types suivants de fichiers avec une
application.

Tableau 11.1 Les fichiers de l’application


Type Extension de nom de fichier
Fichiers programme .EXE et .DLL
Fichiers paquet .BPL et .DCP
Fichiers d’aide .HLP, .CNT et .TOC (si utilisé)
Fichiers ActiveX .OCX (utilise parfois un DLL)
Tables des fichiers locaux .DBF, .MDX, .DBT, .NDX, .DB, .PX, .Y*, .X*, .MB, .VAL, .QBE

Fichiers paquet
Si l’application utilise des paquets d’exécution, il faut distribuer les fichiers
paquet avec l’application. InstallShield Express gère l’installation des fichiers
paquet de la même manière que les DLL, copie ces fichiers et crée les entrées
nécessaires dans les registres Windows. Borland recommande l’installation des
fichiers paquet d’exécution d’origine Borland dans le répertoire Windows\
System. Cela sert d’emplacement commun afin que plusieurs applications
puissent accéder à une seule instance de ces fichiers. Pour les paquets que vous
avez créés, il est recommandé de les installer dans le répertoire de l’application.
Seuls les fichiers .BPL doivent être distribués.
Si vous distribuez des paquets à d’autres développeurs, fournissez les fichiers
.BPL et .DCP.

Contrôles ActiveX
Certains composants fournis avec Delphi sont des contrôles ActiveX. Le
conteneur du composant est lié au fichier exécutable de l’application (ou à un
paquet d’exécution), mais le fichier .OCX du composant doit également être
distribué avec l’application. Ces composants sont :
• Chart FX, copyright par SoftwareFX Inc.
• VisualSpeller Control, copyright par Visual Components, Inc.
• Formula One (tableur), copyright par Visual Components, Inc.
• First Impression (VtChart), copyright par Visual Components, Inc.
• Graph Custom Control, copyright par Bits Per Second Ltd.
Les contrôles ActiveX que vous créez doivent également être enregistrés sur
l’ordinateur cible avant d’être utilisés. Les programmes d’installation comme
InstallShield Express automatisent le processus d’enregistrement. Pour enregistrer
manuellement un contrôle ActiveX, utilisez l’application exempleTRegSvr ou
l’utilitaire Microsoft REGSRV32.EXE (qui n’est pas inclus dans toutes les versions
de Windows).
Les fichiers DLL gérant un contrôle ActiveX doivent également être distribués
avec une application.

Déploiement des applications 11-3


Déploiement d’applications de base de données

Applications complémentaires
Les applications complémentaires sont des programmes distincts en l’absence
desquels votre application Delphi fonctionnerait de manière incomplète ou ne
fonctionnerait pas du tout. Les applications complémentaires peuvent être celles
fournies avec Windows, par InPrise ou des tiers. Le programme utilitaire Server
Manager de InterBase est un exemple de programme complémentaire qui permet
de gérer les utilisateurs et la sécurité des bases de données InterBase.
Si une application dépend d’un programme complémentaire, assurez-vous de le
déployer avec votre application, si c’est possible. La distribution des programmes
complémentaires peut être limitée par des accords de licence de distribution.
Consultez la documentation d’un programme complémentaire pour des
informations spécifiques.

Emplacement des DLL


Vous pouvez installer les fichiers .DLL utilisés par une seule application dans le
même répertoire que l’application. Les DLL utilisées par plusieurs applications
doivent être installées de manière à être partagées par ces applications. La
convention courante veut qu’on installe ces fichiers DLL dans les répertoires
Windows ou Windows\System. Une autre méthode consiste à créer un répertoire
spécifique pour un groupe de fichiers DLL associés comme le fait l’installation
du moteur de bases de données Borland.

Déploiement d’applications de base de données


Les applications accédant à des bases de données présentent des caractéristiques
d’installation propres au-delà de la copie du fichier exécutable de l’application sur
l’ordinateur cible. Le plus souvent, l’accès aux bases de données est géré par un
moteur de bases de données distinct dont les fichiers ne peuvent être liés au fichier
exécutable de l’application. Les applications de bases de données multiniveaux
nécessitent une gestion encore plus spécialisée de l’installation, car les fichiers
constituant l’application doivent être installés sur plusieurs ordinateurs. L’accès
aux bases de données présente deux aspects pour l’installation :
• L’accès au moteur de base de données
• MIDAS, services d’application distribuée multiniveau

L’accès au moteur de bases de données


L’accès aux bases de données dans une application se fait par le biais de divers
moteurs de bases de données. Une application peut utiliser le moteur de bases
de données Borland ou un moteur fourni par un tiers. SQL Links est fourni
(mais pas avec toutes les éditions) pour permettre un accès natif aux systèmes de
bases de données SQL. Les sections suivantes décrivent l’installation des
éléments d’accès aux bases de données d’une application :
• Le moteur de bases de données Borland
• Autres moteurs de bases de données
• SQL Links

11-4 Guide du développeur


Déploiement d’applications de base de données

Le moteur de bases de données Borland


Pour utiliser les composants de données standard Delphi ayant un accès aux
bases de données, le moteur de bases de données Borland (BDE) doit être
présent et disponible. Voir BDEDEPLOY.TXT pour les droits et restrictions
s’appliquant à la distribution du BDE.
Borland recommande l’utilisation de InstallShield Express (ou d’un autre
programme d’installation certifié) pour l’installation du BDE. InstallShield
Express crée les entrées de registre nécessaires et définit les alias nécessaires à
l’application. Il est important d’utiliser un programme certifié pour déployer les
fichiers BDE car :
• Une installation incorrecte du BDE ou des sous-ensembles BDE peut empêcher
le fonctionnement d’autres applications utilisant le BDE. Ces applications sont
des produits Borland, mais également des programmes tiers utilisant le BDE.
• Sous Windows 95 et Windows NT, les informations de configuration BDE sont
stockées dans les registres Windows et non pas dans des fichiers .INI , comme
c’était le cas avec Windows 16 bits. La création ou la suppression de ces
entrées lors de l’installation ou de la désinstallation est une tâche complexe.
Il est possible de n’installer que la partie du BDE nécessaire à une application.
Si, par exemple, une application n’utilise que des tables Paradox, il est seulement
nécessaire d’installer la partie du BDE indispensable à l’accès aux tables Paradox.
Cela réduit l’espace disque nécessaire à une application. Les programmes
d’installation certifiés, comme InstallShield Express, sont capables d’effectuer une
installation partielle du BDE. Il faut prendre garde à laisser intact les fichiers
système BDE inutilisés par l’application installée mais nécessaires à d’autres
programmes déjà installés.

Autres moteurs de bases de données


Vous pouvez utiliser des moteurs de bases de données fournis par des tiers pour
gérer l’accès aux bases de données d’applications Delphi. Consultez la
documentation ou le vendeur du moteur de bases de données pour ce qui
concerne les problèmes de droit, d’installation et de configuration du moteur.

SQL Links
SQL Links propose les pilotes permettant de connecter une application au
logiciel client d’une base de données SQL (via le moteur de bases de données
Borland). Voir DEPLOY.TXT pour les droits et restrictions spécifiques
s’appliquant à la redistribution de SQL Links. Comme pour le moteur de bases
de données Borland, SQL Links doit être déployé en utilisant InstallShield
Express (ou tout autre programme certifié).
Remarque SQL Links connecte le BDE au logiciel client et pas directement à la base de
données SQL même. Il est donc toujours nécessaire d’installer le programme
client du système de bases de données SQL utilisé. Reportez-vous à la
documentation de votre système SQL ou consultez le vendeur pour davantage
d’informations sur l’installation et la configuration du logiciel client.

Déploiement des applications 11-5


Déploiement d’applications de base de données

Le tableau suivant présente les noms des fichiers de pilote et de configuration


utilisés par SQL Links pour se connecter aux différents systèmes de base de
données SQL. Ces fichiers sont fournis avec SQL Links et sont redistribuables en
accord avec la licence Delphi.

Tableau 11.2 Fichiers des logiciels client des bases de données SQL
Vendeur Fichiers redistribuables
Oracle 7 SQLORA32.DLL et SQL_ORA.CNF
Oracle8 SQLORA8.DLL et SQL_ORA8.CNF
Sybase Db-Lib SQLSYB32.DLL et SQL_SYB.CNF
Sybase Ct-Lib SQLSSC32.DLL et SQL_SSC.CNF
Microsoft SQL Server SQLMSS32.DLL et SQL_MSS.CNF
Informix 7 SQLINF32.DLL et SQL_INF.CNF
Informix 9 SQLINF9.DLL et SQL_INF9.CNF
DB/2 SQLDB232.DLL et SQL_DB2.CNF
InterBase SQLINT32.DLL et SQL_INT.CNF

Installez SQL Links en utilisant InstallShield Express ou tout autre programme


d’installation certifié. Pour des informations spécifiques concernant l’installation
et la configuration de SQL Links, voir le fichier d’aide SQLLNK32.HLP qui est
installé, par défaut, dans le répertoire principal du BDE.

MIDAS, services d’application distribuée multiniveau


Les services d’application distribuée multiniveau (MIDAS) sont composés de
Business Object Broker, OLEnterprise, Remote DataBroker et ConstraintBroker
Manager (explorateur SQL). MIDAS offre à des applications Delphi des
fonctionnalités multiniveaux.
L’installation de l’exécutable et des fichiers associés d’une application
multiniveau se gère de la même manière que pour une application généraliste.
Certains des fichiers constituant MIDAS doivent être installés sur l’ordinateur
client et d’autres sur l’ordinateur serveur. Pour des informations sur l’installation
standard d’applications, voir “Déploiement d’applications généralistes” à la
page 11-1. Pour des informations spécifiques concernant la licence et les droits de
distribution de MIDAS, voir le fichier texte LICENSE.TXT sur le CD MIDAS et le
fichier DEPLOY.TXT de Delphi.
MIDAS.DLL doit être installé sur l’ordinateur client et enregistré dans Windows.
Sur le serveur, les fichiers MIDAS.DLL et STDVCL40.DLL doivent être installés
et enregistrés pour Remote DataBroker et le fichier DBEXPLOR.EXE pour
ConstraintBroker. Les programmes d’installation comme InstallShield Express
automatisent le processus d’enregistrement de ces DLL. Pour enregistrer
manuellement les DLL, utilisez l’application exemple TRegSvr ou le programme
utilitaire Microsoft REGSRV32.EXE (qui n’est pas proposé avec toutes les
versions de Windows).

11-6 Guide du développeur


Déploiement d’applications Web

Le CD de déploiement MIDAS contient les programme d’installation pour les


parties client et serveur de OLEnterprise et de Business ObjectBroker. Utilisez
uniquement l’initialisateur de configuration du CD MIDAS pour installer
OLEnterprise.
La liste suivante indique les fichiers nécessaires devant être installés sur la
machine serveur.
UNINSTALL.EXE OBJFACT.ICO W32PTHD.DLL NBASE.IDL
LICENSE.TXT ODEBKN40.DLL RPMARN40.DLL OBJX.EXE
README.TXT ODECTN40.DLL RPMAWN40.DLL OLECFG.EXE
OLENTER.HLP RPMEGN40.DLL RPMCBN40.DLL OLEWAN40.CAB
OLENTER.CNT ODEDIN40.DLL RPMCPN40.DLL OLENTEXP.EXE
FILELIST.TXT ODEEGN40.DLL BROKER.EXE OLENTEXP.HLP
SETLOG.TXT ODELTN40.DLL RPMFEN40.DLL OLENTEXP.CNT
SETLOG.EXE LIBAVEMI.DLL RPMUTN40.DLL BRKCP.EXE
OBJPING.EXE OLEAAN40.DLL RPMFE.CAT BROKER.ICO
OBJFACT.EXE OLERAN40.DLL EXPERR.CAT

La liste suivante indique les fichiers devant être installés sur la machine client.
NBASE.IDL ODEN40.DLL RPMFEN40.DLL OLENTEXP.EXE
ODECTN40.DLL RPMARN40.DLL RPMUTN40.DLL SETLOG.EXE
ODEDIN40.DLL RPMAWN40.DLL OLERAN40.DLL OLECFG.EXE
ODEEGN40.DLL RPMCBN40.DLL OLEAAN40.DLL W32PTHD.DLL
ODELTN40.DLL RPMCPN40.DLL OLEWAN40.CAB
ODEMSG.DLL RPMEGN40.DLL OBJX.EXE

Déploiement d’applications Web


Certaines applications Delphi sont conçues pour être exécutées sur le Web, sous
la forme de DLL ISAPI, d’extension côté serveur, d’applications CGI ou de fiches
ActiveForm.
Les étapes de l’installation d’applications Web sont identiques à celles des
applications généralistes à cette différence que les fichiers de l’application sont
déployés sur le serveur Web. Pour des informations sur l’installation de
programmes standard, voir “Déploiement d’applications généralistes” à la
page 11-1.
Les considérations suivantes sont spécifiques au déploiement d’applications
Web :
• Pour les applications de bases de données, le moteur de bases de données
Borland (ou tout autre moteur de bases de données) doit être installé avec les
fichiers de l’application sur le serveur Web.

Déploiement des applications 11-7


Programmer pour des environnements hôtes hétérogènes

• La sécurité définie pour les répertoires ne doit pas être trop restrictive afin
que soit possible l’accès aux fichiers de l’application, au BDE ou aux fichiers
de données.
• Le répertoire contenant une application doit avoir des attributs de lecture et
d’exécution.
• L’application ne doit pas utiliser de chemins d’accès codés “en dur” pour
accéder aux bases de données et aux autres fichiers.
• L’emplacement d’un contrôle ActiveX est indiqué par le paramètre
CODEBASE de la balise HTML <OBJECT>.

Programmer pour des environnements hôtes


hétérogènes
En raison des caractéristiques de l’environnement Windows, certains éléments
peuvent varier selon les préférences de l’utilisateur ou la configuration. Les points
suivants peuvent affecter le déploiement d’une application sur un autre ordinateur :
• Résolution d’écran et profondeurs de couleur
• Fontes
• Versions de Windows
• Applications complémentaires
• Emplacement des DLL

Résolution d’écran et profondeurs de couleur


La taille du bureau Windows et le nombre de couleurs disponibles sur un
ordinateur est configurable et dépend du matériel installé. Il est probable que ces
caractéristiques ne sont pas identiques sur les systèmes utilisés pour le
développement et ceux sur lesquels l’application est déployée.
L’aspect d’une application (fenêtres, objets et taille des fontes) sur des
ordinateurs utilisant des résolutions différentes peut être géré de différentes
manières :
• Concevez l’application avec la plus basse résolution employée par les
utilisateurs (généralement, 640x480). Il n’y a rien à faire dans ce cas pour
redimensionner les objets dynamiquement afin de les rendre proportionnels à
la taille d’affichage de l’écran du système hôte. Visuellement, plus la
résolution est importante et plus les objets apparaissent petits.
• Effectuez la conception en utilisant la résolution du système employé pour
effectuer le développement et, à l’exécution, redimensionnez dynamiquement
toutes les fiches et les objets proportionnellement à la différence de résolution
écran entre le système de développement et le système hôte (en utilisant un
coefficient de variation entre les résolutions écran).

11-8 Guide du développeur


Programmer pour des environnements hôtes hétérogènes

• Effectuez la conception en utilisant une résolution du système de


développement et, à l’exécution, redimensionnez dynamiquement les fiches de
l’application. Selon la position des contrôles visuels dans les fiches, cette
option peut nécessiter que les fiches disposent de barres de défilement pour
que l’utilisateur puisse accéder à tous les contrôles des fiches.

Si vous n’utilisez pas de redimensionnement dynamique


Si les fiches et les contrôles visuels constituant une application ne sont pas
redimensionnés dynamiquement à l’exécution, concevez les éléments de
l’application en utilisant la résolution la plus basse. Sinon, les fiches d’une
application exécutée sur un ordinateur utilisant une résolution d’écran plus faible
que celle utilisée pour le système de développement risquent de déborder de
l’écran.
Si par exemple, le système de développement est configuré avec une résolution
écran de 1024x768 et qu’une fiche est conçue avec une largeur de 700 pixels, une
partie de cette fiche ne sera pas visible sur le bureau Windows d’un ordinateur
configuré avec une résolution de 640x480.

Si vous redimensionnez dynamiquement les fiches et les


contrôles
Si les fiches et les contrôles visuels d’une application sont dynamiquement
redimensionnés, adaptez tous les aspects du processus de redimensionnement
pour garantir un aspect optimal de l’application pour toutes les résolutions écran
possibles. Voici quelques facteurs à considérer lorsque vous redimensionnez
dynamiquement les éléments visuels d’une application :
• Le redimensionnement des fiches et des contrôles visuels est effectué en
utilisant un ratio calculé en comparant la résolution écran du système de
développement à celle du système sur lequel l’application est installée. Utilisez
une constante pour représenter une dimension de la résolution écran du
système de développement : la hauteur ou la largeur exprimée en pixels.
Récupérez à l’exécution la même dimension pour le système de l’utilisateur en
utilisant la propriété TScreen.Height ou TScreen.Width . Divisez la valeur pour
le système de développement par la valeur pour le système de l’utilisateur
afin d’en dériver le ratio entre les résolutions écran des deux systèmes.
• Redimensionnez les éléments visuels de l’application (fiches et contrôles) en
réduisant ou en augmentant la taille des éléments et leur position dans les
fiches. Ce redimensionnement est proportionnel à la différence entre les
résolutions écran des systèmes du développeur et de l’utilisateur.
Redimensionnez et repositionnez automatiquement les contrôles visuels des
fiches en affectant la valeur True à la propriété CustomForm.Scaled et en
appelant la méthode TWincontrol.ScaleBy. La méthode ScaleBy ne modifie pas
la hauteur ou la largeur de la fiche. Il faut effectuer cette opération
manuellement en multipliant les valeurs en cours des propriétés Height et
Width par le ratio de différence des résolutions écran.

Déploiement des applications 11-9


Programmer pour des environnements hôtes hétérogènes

• Les contrôles d’une fiche peuvent être redimensionnés manuellement au lieu


d’utiliser la méthode TWincontrol.ScaleBy d’une fiche, en faisant référence à
chaque contrôle dans une boucle et en affectant ses dimensions et sa position.
La valeur des propriétés Height et Width des contrôles visuels est multipliée
par le ratio de différence des résolutions écran. Repositionnez les contrôles
visuels en fonction de la résolution écran en multipliant la valeur des
propriétés Top et Left par le même ratio.
• Si une application a été conçue sur un ordinateur configuré pour une
résolution écran supérieure à celle de l’utilisateur, les tailles de fontes seront
réduites dans le processus de redimensionnement des contrôles visuels. Si la
taille de la fonte lors de la conception est petite, la fonte redimensionnée à
l’exécution risque d’être trop petite pour être lisible. Par exemple, supposons
que la taille de fonte par défaut d’une fiche est 8. Avec un système de
développement ayant une résolution écran de 1024x768 et celui de l’utilisateur
une résolution 640x480, les contrôles visuels seront réduits d’un facteur 0,625
(640 / 1024 = 0.625). La taille de fonte d’origine de 8 est réduite à 5 (8 * 0,625
= 5). Le texte de l’application apparaît irrégulier et illisible quand Windows
l’affiche avec la fonte réduite.
• Certains contrôles visuels comme TLabel et TEdit se redimensionnent
dynamiquement quand la taille de la fonte du contrôle change. Cela peut
affecter les applications déployées quand les fiches et les contrôles sont
redimensionnés dynamiquement. Le redimensionnement du contrôle provoqué
par la modification de taille de la fonte se cumule à la modification de taille
due au redimensionnement proportionnel aux résolutions écran. Cet effet
indésirable est neutralisé en affectant la valeur False à la propriété AutoSize de
ces contrôles.
• Il faut éviter d’utiliser des coordonnées en pixel explicites, par exemple pour
écrire directement dans un canevas. Il faut à la place modifier les coordonnées
en leur appliquant un ratio proportionnel au ratio de différence des
résolutions écran entre le système de développement et celui d’utilisation. Si,
par exemple, l’application dessine un rectangle dans le canevas de dix pixels
de haut sur vingt pixels de large, multipliez les valeurs dix et vingt par le
ratio de différence de résolution. Ainsi, vous êtes certain que le rectangle
apparaît visuellement de la même taille pour différentes résolutions écran.

Adaptation à des profondeurs de couleur variables


Pour prendre en compte le fait que tous les ordinateurs sur lesquels l’application
est déployée ne sont pas configurés avec les mêmes possibilités de couleurs, la
solution la plus simple consiste à n’utiliser que des graphiques avec le plus petit
nombre possible de couleurs. Cela s’applique particulièrement aux glyphes des
contrôles qui doivent utiliser des graphiques en 16 couleurs. Pour l’affichage
d’images, vous pouvez soit proposer plusieurs copies de l’image dans différentes
résolutions et niveaux de couleur ou indiquer dans l’application la résolution
minimale et le nombre de couleurs nécessaires à l’application.

11-10 Guide du développeur


Programmer pour des environnements hôtes hétérogènes

Fontes
Windows dispose d’un jeu standard de fontes TrueType et vectorielles. Quand
vous concevez une application devant être déployée sur d’autres ordinateurs,
tenez compte du fait que tous les ordinateurs n’ont pas nécessairement de fontes
en-dehors du jeu Windows standard.
Les composants texte utilisés dans l’application ne doivent utiliser que des fontes
qui sont très probablement disponibles sur les ordinateurs cible.
Quand l’utilisation d’une fonte non standard est absolument nécessaire dans une
application, vous devez distribuer cette fonte avec l’application. Soit le
programme d’installation, soit l’application même doit installer la fonte sur
l’ordinateur cible. La distribution de fontes créées par des tiers peut être sujette à
des restrictions imposées par leurs créateurs.
Windows dispose d’une protection contre l’utilisation d’une fonte inexistante sur
un système. Il lui substitue une fonte existante, la plus proche possible. Bien que
cela empêche les erreurs dues à des fontes manquantes, le résultat final peut
dégrader l’aspect visuel de l’application. Il est préférable de prévoir cette
éventualité à la conception.
Pour mettre à la disposition d’une application une fonte non standard, utilisez
les fonctions AddFontResource et DeleteFontResource de l’API Windows. Déployez
les fichiers .FOT des fontes non-standard avec l’application.

Versions de Windows
Quand vous utilisez des fonctions de l’API Windows ou si vous accédez à des
zones du système d’exploitation Windows dans une application, il y a le risque
que cette fonction, cette opération ou cette zone ne soit pas disponible sur des
ordinateurs utilisant une version différente de Windows. Par exemple, les
services n’ont de sens que pour le système d’exploitation Windows NT. Si une
application doit se comporter comme un service ou doit interagir avec un
service, elle ne fonctionnera pas si l’application est installée sous Windows 95.
Pour prendre cette possibilité en compte, vous avez différentes possibilités :
• Spécifiez dans les spécifications logicielles de l’application les versions de
Windows sous lesquelles l’application peut s’exécuter. C’est alors à l’utilisateur
de n’installer et de n’utiliser l’application que dans des versions compatibles
de Windows.
• Testez la version de Windows lors de l’installation de l’application. Si une
version incompatible de Windows est détectée, arrêtez le processus
d’installation ou prévenez l’utilisateur du problème.
• Testez la version de Windows à l’exécution, juste avant d’exécuter une
opération qui n’est pas applicable à toutes les versions. Si une version
incompatible de Windows est détectée, abandonnez l’opération et informez
l’utilisateur. Vous pouvez aussi utiliser du code différent pour les différentes
versions de Windows. Certaines opérations s’effectuent différemment sous
Windows 95 et sous Windows NT. Utilisez la fonction GetVersionEx de l’API
Windows pour déterminer la version de Windows.

Déploiement des applications 11-11


Termes du contrat de licence logicielle

Termes du contrat de licence logicielle


La distribution de certains des fichiers associés aux applications Delphi est
sujette à des limitations ou est purement et simplement interdite. Les documents
suivants décrivent les stipulations légales concernant la redistribution de ces
fichiers quand il y a des restrictions :
• DEPLOY.TXT
• README.TXT
• Le contrat de licence
• Documentation de produits vendus par un tiers

DEPLOY.TXT
DEPLOY.TXT aborde certains aspects légaux de la distribution de divers
composants et utilitaires et autres produits pouvant faire partie ou être associés à
une application C++BuilderDelphi. DEPLOY.TXT est un fichier texte installé dans
le répertoire principal de C++BuilderDelphi. Il aborde les sujets suivants,
• Les fichiers .EXE, .DLL et .BPL
• Les composants et les paquets de conception
• Le moteur de bases de données Borland (BDE)
• Les contrôles ActiveX
• Les images exemple
• MIDAS
• SQL Links

README.TXT
README.TXT contient des informations de dernière minute sur
C++BuilderDelphi ; il peut donc contenir des informations pouvant affecter les
droits de redistribution des composants, utilitaires ou autres éléments.
README.TXT est un fichier d’aide Windows installé dans le répertoire principal
de C++BuilderDelphi.

Contrat de licence
Le contrat de licence C++BuilderDelphi est un document imprimé qui traite des
droits et obligations légales concernant C++BuilderDelphi.

Documentation de produits vendus par un tiers


Les droits de redistribution des composants, utilitaires, applications utilitaires,
moteurs de bases de données ou autres logiciels provenant d’un tiers sont régis
par le vendeur fournissant le produit. Consultez la documentation du produit ou
le vendeur pour des informations concernant la redistribution du produit avec
une application C++BuilderDelphi avant de la distribuer.

11-12 Guide du développeur


Partie

II
Développement d’applications
Part II

de base de données
Les chapitres de cette partie présentent les concepts et les connaissances
nécessaires à la création d’applications de base de données Delphi.
Remarque Vous avez besoin de l’édition Professionnelle ou Entreprise de Delphi pour
développer des applications de base de données. Pour implémenter des bases de
données Client/Serveur plus évoluées, vous avez besoin des caractéristiques de
Delphi disponibles dans l’édition Entreprise.

Développement d’applications de base de données


Chapitre

Conception d’applications de
Chapter 12
12
bases de données
Les applications de bases de données permettent aux utilisateurs d’interagir avec
les informations stockées dans les bases de données. Les bases de données
permettent de structurer les informations et de les partager entre plusieurs
applications.
Delphi permet de gérer les applications de bases de données relationnelles. Les
bases de données relationnelles organisent les informations en tables, qui
contiennent des lignes (enregistrements) et des colonnes (champs). Ces tables
peuvent être manipulées par des opérations simples appelées calculs relationnels.
Lorsque vous concevez une application de bases de données, vous devez
comprendre comment les données sont structurées. A partir de cette structure,
vous pouvez concevoir une interface utilisateur pour afficher les données et
permettre à l’utilisateur d’entrer de nouvelles informations et de modifier les
données existantes.
Ce chapitre présente certains aspects courants de la conception d’une application
de bases de données et les décisions inhérentes à la conception d’une interface
utilisateur.

Conception d’applications de bases de données 12-1


Utilisation des bases de données

Utilisation des bases de données


Les composants de la page Accès BD, de la page ADO ou de la page InterBase
de la palette des composants permettent à votre application de lire les bases de
données et d’y écrire. Les composants de la page Accès BD utilisent le moteur
de bases de données Borland pour accéder aux informations de la base de
données et pour rendre ces dernières accessibles aux contrôles orientés données
dans votre interface utilisateur. Les composants de la page ADO utilisent les
objets ADO (ActiveX Data Objects) pour accéder aux informations de la base de
données via OLEDB. Les composants de la page InterBase accèdent directement
à une base de données InterBase.
Suivant votre version de Delphi, le moteur de bases de données Borland
comprend des pilotes pour différents types de bases de données. Bien que tous
ces types de bases de données contiennent des tables qui stockent des
informations, certains présentent certaines caractéristiques telles que
• Sécurité des bases de données.
• Transactions.
• Dictionnaire de données.
• Intégrité référentielle, procédures stockées et déclencheurs.

Types de bases de données


Vous pouvez vous connecter à différents types de bases de données, suivant les
pilotes installés avec le BDE ou ADO .
Ces pilotes peuvent connecter vos applications aux bases de données locales,
comme Paradox, Access et dBASE, ou aux serveurs de bases de données distants,
comme Microsoft SQL Server, Oracle et Informix. De même, les composants
InterBase Express peuvent accéder à une version locale ou distante d’InterBase.
Remarque Différentes versions de Delphi possèdent des composants qui utilisent ces pilotes
(BDE ou ADO), ou des composants InterBase express.
Le choix du type de base de données à utiliser dépend de plusieurs facteurs. Il
se peut que vos données soient déjà stockées dans une base de données
existante. Si vous créez les tables d’informations qu’utilise votre application, les
points suivants vous intéressent.
• Quelle quantité de données les tables contiendront-elles ?
• Combien d’utilisateurs partageront ces tables ?
• Quel type de performance (vitesse) attendez-vous de la base de données ?

12-2 Guide du développeur


Utilisation des bases de données

Bases de données locales


Les bases de données locales résident sur votre disque local ou sur un réseau
local. Elles disposent d’interfaces de programmation d’applications propriétaires
pour accéder aux données. Souvent, elles sont dédiées à un seul système.
Lorsqu’elles sont partagées par plusieurs utilisateurs, elles utilisent des
mécanismes de verrouillage de fichiers. C’est pourquoi elles sont parfois appelées
bases de données à base de fichiers.
Les bases de données locales peuvent être plus rapides que les serveurs de bases
de données distants car elles résident souvent sur le même système que
l’application de bases de données.
Comme elles sont basées sur les fichiers, les bases de données locales sont plus
limitées que les serveurs de bases de données distants dans la quantité de
données qu’elles peuvent stocker. En conséquence, pour savoir si vous devez
utiliser ou non une base de données locale, vous devez envisager la quantité de
données que contiendront les tables.
Les applications qui utilisent des bases de données locales sont appelées
applications à niveau unique car l’application et la base de données partagent un
système de fichiers unique.
Paradox, dBASE, FoxPro et Access sont des exemples de bases de données
locales.

Serveurs de bases de données distants


Les serveurs de bases de données distants résident généralement sur une
machine distante. Ils utilisent SQL (Structured Query Language) pour permettre
aux clients d’accéder aux données. C’est pourquoi ils sont parfois appelés
serveurs SQL (ils sont aussi appelés système de gestion de bases de données
distant). Outre les commandes courantes qui composent SQL, la plupart des
serveurs de bases de données distants gère une variante unique du langage SQL.
Les serveurs de bases de données distants sont conçus pour permettre à
plusieurs utilisateurs d’accéder simultanément aux informations. Au lieu d’un
système de verrouillage à base de fichiers tel que ceux utilisés par les bases de
données locales, ils offrent un support multi-utilisateur plus élaboré, basé sur les
transactions.
Les serveurs de bases de données distants contiennent davantage de données
que les bases de données locale. Parfois, les données d’un serveur de bases de
données distant ne résident pas sur une seule machine mais sont réparties entre
plusieurs serveurs.
Les applications qui utilisent des serveurs de bases de données distants sont
appelées applications à niveau double ou applications multiniveaux car
l’application et la base de données fonctionnent sur des systèmes (ou niveaux)
indépendants.
InterBase, Oracle, Sybase, Informix, Microsoft SQL server et DB2 sont des
exemples de serveurs SQL.

Conception d’applications de bases de données 12-3


Utilisation des bases de données

Sécurité des bases de données


Les bases de données contiennent souvent des informations sensibles. Différentes
bases de données offrent des schémas de sécurité pour protéger ces informations.
Certaines bases de données, comme Paradox et dBASE, n’offrent une protection
qu’au niveau des tables ou des champs. Lorsque les utilisateurs essaient
d’accéder aux tables protégées, ils doivent fournir un mot de passe. Une fois
identifiés, ils ne peuvent visualiser que les champs (colonnes) pour lesquels ils
disposent d’une permission.
La plupart des serveurs SQL requièrent un mot de passe et un nom d’utilisateur
pour être utilisés. Une fois que l’utilisateur est connecté à la base de données, le
nom d’utilisateur et le mot de passe déterminent les tables qu’il peut utiliser.
Pour plus d’informations sur l’attribution de mots de passe pour accéder aux
serveurs SQL en utilisant le BDE, voir “Contrôle de la connexion au serveur” à
la page 17-7. Pour plus d’informations sur la fourniture de ces informations en
utilisant ADO , voir “Contrôle de la boîte de dialogue d’identification” à la
page 23-8. Pour plus d’informations sur la fourniture de ces informations en
utilisant les composants d’accès direct à InterBase, voir l’événement OnLogin de
TIBDatabase.
Lorsque vous concevez des applications de bases de données, vous devez
envisager le type d’authentification requis par votre serveur de base de données.
Si vous ne souhaitez pas que vos utilisateurs aient besoin de fournir un mot de
passe, vous devez soit utiliser une base de données qui n’en requiert pas, soit
fournir le mot de passe et le nom d’utilisateur au serveur par programmation.
Lorsque vous fournissez le mot de passe par programmation, vous devez veiller
à ce que la sécurité ne soit pas violée par lecture du mot de passe à partir de
l’application.
Si vous obligez les utilisateurs à fournir un mot de passe, vous devez déterminer
à quel moment ce dernier est requis. Si vous utilisez une base de données locale
mais envisagez de passer à un serveur SQL plus important, vous pouvez inviter
l’utilisateur à fournir son mot de passe avant d’accéder à la table, même si pour
le moment cela ne s’impose pas.
Si votre application requiert plusieurs mots de passe pour la connexion à
plusieurs bases de données ou systèmes protégés, vous pouvez demander aux
utilisateurs de fournir un mot de passe maître unique qui permet d’accéder à
une table de mots de passe requis par ces systèmes. L’application fournit alors
les mots de passe par programmation, sans que les utilisateurs aient besoin de
fournir plusieurs mots de passe.
Dans les applications multiniveaux, vous pouvez utiliser un modèle de sécurité
différent. Vous pouvez utiliser HTTPs, CORBA ou MTS pour contrôler l’accès
aux niveaux intermédiaires et laisser ces derniers gérer tous les détails relatifs à
l’accès aux serveurs de bases de données.

12-4 Guide du développeur


Utilisation des bases de données

Transactions
Une transaction est un groupe d’actions qui doivent être menées avec succès sur
une ou plusieurs tables dans une base de données avant d’être validées (rendues
définitives). Si l’une des actions du groupe échoue, toutes les actions sont
abandonnées (annulées).
Les transactions protègent contre les défaillances matérielles qui se produisent au
milieu d’une commande de base de données ou d’un ensemble de commandes.
Elle constituent aussi la base du contrôle simultané de plusieurs utilisateurs sur
les serveurs SQL. Lorsque tous les utilisateurs interagissent avec la base de
données par le biais de transactions, les commandes d’un utilisateur ne peuvent
pas altérer l’unité d’une transaction d’un autre utilisateur ; le serveur SQL
planifie les transactions entrantes, qui réussissent ou échouent en bloc.
Bien que le support des transactions ne fasse pas partie de la plupart des bases
de données locales, les pilotes du moteur de bases de données Borland offrent
pour certaines un support des transactions limité. Pour les serveurs SQL et les
bases de données de type ODBC, le support des transactions de base de données
est fourni par le composant qui représente la connexion à la base de données.
Dans les applications multiniveaux, vous pouvez créer des transactions qui
comprennent des actions autres que des opérations de base de données ou qui
englobent plusieurs bases de données.
Pour plus de détails sur l’utilisation des transactions dans les applications basées
sur le moteur de bases de données Borland, voir “Utilisation des transactions” à
la page 13-5. Pour plus de détails sur l’utilisation des transactions dans les
applications basées sur ADO, voir “Utilisation des transactions de connexion” à
la page 23-11. Pour plus de détails sur l’utilisation des transactions dans les
applications multiniveaux, voir “Gestion des transactions dans les applications
multiniveaux” à la page 14-29 . Pour plus de détails sur l’utilisation des
transactions dans les applications qui utilisent les composants d’accès direct à
InterBase, voir l’aide en ligne pour le composant TIBTransaction.

Dictionnaire de données
Quand vous utilisez le BDE pour accéder à vos données, votre application peut
accéder au dictionnaire de données. Le dictionnaire de données offre une zone
de stockage paramétrable, indépendante de vos applications, dans laquelle vous
pouvez créer des ensembles d’attributs de champ étendus qui décrivent le
contenu et l’aspect des données.
Par exemple, si vous êtes souvent amené à développer des applications
financières, vous pouvez créer un certain nombre d’ensembles spécialisés
d’attributs de champ pour décrire les différents formats d’affichage monétaire. Si
ensuite, lors de la conception, vous créez un ensemble de données dans une
application, plutôt que d’utiliser l’inspecteur d’objets pour définir manuellement
les champs monétaires de l’ensemble de données, vous pouvez associer ces
champs à un ensemble d’attributs de champ étendus dans le dictionnaire de
données. L’utilisation du dictionnaire de données permet d’homogénéiser l’aspect
des données sur toutes les applications que vous créez.

Conception d’applications de bases de données 12-5


Utilisation des bases de données

Dans un environnement client/serveur, le dictionnaire de données peut se situer


sur un serveur distant afin d’offrir un meilleur partage des informations.
Pour savoir, lors de la conception, comment créer des ensembles d’attributs à
partir de l’éditeur de champs, puis associer ces attributs aux champs des
ensembles de données de votre application, voir “Création d’ensembles
d’attributs pour les composants champ” à la page 19-17. Pour savoir comment
créer un dictionnaire de données ainsi que des attributs de champ étendus en
utilisant l’explorateur SQL et l’explorateur de bases de données, reportez-vous à
leur aide en ligne respective.
Une interface de programmation pour le dictionnaire de données est disponible
dans l’unité drintf (dans le répertoire lib). Cette interface fournit les méthodes
suivantes :

Tableau 12.1 Interface du dictionnaire de données


Routine Utilisation
DictionaryActive Indique si le dictionnaire de données est actif.
DictionaryDeactivate Désactive le dictionnaire de données.
IsNullID Indique si un Id donné est null
FindDatabaseID Renvoie l’Id d’une base de données en fonction de son alias.
FindTableID Renvoie l’Id d’une table d’une base de données spécifiée.
FindFieldID Renvoie l’Id d’un champ d’une table spécifiée.
FindAttrID Renvoie l’Id d’un ensemble d’attributs nommé.
GetAttrName Renvoie le nom d’un ensemble d’attributs en fonction de son Id.
GetAttrNames Exécute un rappel de chaque ensemble d’attributs du dictionnaire.
GetAttrID Renvoie l’Id de l’ensemble d’attributs pour un champ spécifié.
NewAttr Crée un nouvel ensemble d’attributs à partir d’un composant
champ.
UpdateAttr Met à jour un ensemble d’attributs pour établir une
correspondance avec les propriétés d’un champ.
CreateField Crée un composant champ à partir d’attributs stockés.
UpdateField Modifie les propriétés d’un champ pour établir une
correspondance avec un ensemble d’attributs spécifié.
AssociateAttr Associe un ensemble d’attributs à un Id de champ donné.
UnassociateAttr Supprime l’association d’un ensemble d’attributs d’un Id de
champ.
GetControlClass Renvoie la classe contrôle d’un Id d’attribut spécifié.
QualifyTableName Renvoie un nom de table totalement qualifié (qualifié par le
nom d’utilisateur).
QualifyTableNameByName Renvoie un nom de table totalement qualifié (qualifié par le
nom d’utilisateur).
HasConstraints Indique si l’ensemble de données possède des contraintes dans
le dictionnaire.
UpdateConstraints Met à jour les contraintes importées d’un ensemble de données.
UpdateDataset Met à jour un ensemble de données en fonction des paramètres
et des contraintes appliquées dans le dictionnaire.

12-6 Guide du développeur


Architecture des bases de données

Intégrité référentielle, procédures stockées et


déclencheurs
Toutes les bases de données relationnelles présentent certaines caractéristiques
communes qui permettent aux applications de stocker et de manipuler les
données. En outre, les bases de données offrent souvent des fonctionnalités qui
leur sont propres et qui s’avèrent utiles pour garantir la cohérence des relations
entre les tables d’une base de données.
Ces fonctionnalités sont les suivantes :
• Intégrité référentielle. L’intégrité référentielle offre un mécanisme permettant
d’éviter la cassure des relations maître/détail entre les tables. Lorsque
l’utilisateur essaie de supprimer un champ de la table maître, pouvant aboutir
à la création d’enregistrements détail orphelins, les règles de l’intégrité
référentielle évitent la suppression ou suppriment automatiquement les
enregistrements détail orphelins.
• Procédures stockées. Les procédures stockées sont des ensembles
d’instructions SQL nommées et stockées sur un serveur SQL. Les procédures
stockées réalisent généralement des tâches de base de données courantes sur le
serveur et renvoient des ensembles d’enregistrements (ensembles de données).
• Déclencheurs. Les déclencheurs sont des ensembles d’instructions SQL
automatiquement créées en réponse à une commande particulière.

Architecture des bases de données


Les applications de base de données sont construites à partir d’éléments
d’interface utilisateur, de composants qui gèrent la ou les bases de données et de
composants qui représentent les données contenues dans les tables de ces bases
de données (ensembles de données). L’architecture de votre application de base
de données représente l’organisation de tous ces éléments.
En isolant les composants d’accès aux bases de données dans des modules de
données, vous pouvez concevoir des formulaires dans vos applications de base
de données qui offrent une interface homogène. Les liens aux fiches et aux
modules de données convenablement conçus dans le référentiel d’objets permet
aux développeurs de construire à partir des fondations existantes plutôt de que
commencer tout projet de zéro. Le partage des formulaires et des modules vous
permet également de développer des standards d’entreprise pour l’accès aux
bases de données et les interfaces des applications.
De nombreux aspects de l’architecture de votre application de base de données
dépendent du type de bases de données que vous utilisez, du nombre
d’utilisateurs qui partageront les informations des bases de données et du type
d’informations que vous manipulez. Voir “Types de bases de données” à la
page 12-2 pour plus d’informations sur les différents types de bases de données.

Conception d’applications de bases de données 12-7


Architecture des bases de données

Si les informations ne sont pas destinées à être partagées, vous pouvez utiliser
une base de données locale dans une application à niveau unique. Cette approche
présente l’avantage de la rapidité (car les données sont stockées localement) et ne
nécessite pas l’achat d’un serveur de bases de données séparé ni de licences de
site onéreuses. Toutefois, elle est limitée par la quantité de données pouvant être
contenues dans les tables et par le nombre d’utilisateurs pouvant être pris en
charge par votre application.
L’écriture d’une application à niveau double permet de gérer davantage
d’utilisateurs et d’utiliser des bases de données volumineuses distantes stockant
beaucoup plus d’informations.
Remarque La gestion des applications à niveau double requiert SQL Links, InterBase ou
ADO .
Lorsque les informations de base de données comprennent des relations
complexes entre plusieurs tables ou que le nombre de clients s’accroît, vous
pouvez utiliser une application multiniveau. Les applications multiniveaux
comprennent des niveaux intermédiaires qui centralisent la logique qui gouverne
les interactions avec votre base de données et offrent ainsi un contrôle centralisé
des relations entre les données. Cela permet à différentes applications clientes
d’utiliser les mêmes données tout en garantissant l’homogénéité de la logique
des données. Les applications multiniveaux autorisent aussi les applications
clientes de taille réduite car la majeure partie du traitement est déplacée vers les
niveaux intermédiaires. Ces applications clientes de taille réduite sont plus faciles
à installer, à configurer et à gérer car elles ne comprennent pas de logiciel de
connectivité de base de données. Les applications multiniveaux peuvent aussi
améliorer la performance en répartissant les tâches de traitement des données sur
plusieurs systèmes.

Anticipation de l’évolutivité
Le processus de développement peut s’avérer plus prenant et onéreux au fur et à
mesure que le nombre de niveaux s’accroît. C’est pourquoi vous pouvez
commencer par développer une application à niveau unique. L’augmentation de
la quantité de données, du nombre d’utilisateurs et du nombre des différentes
applications accédant aux données vous obligera peut-être à adopter une
architecture multiniveau. En anticipant l’évolutivité, vous pouvez protéger vos
investissements en développement d’applications à niveau unique ou à niveau
double par la réutilisation de votre code au fur et à mesure que votre application
s’accroît.
Les composants orientés données de la VCL facilitent l’écriture d’applications
évolutives en cernant le comportement de la base de données et des données
qu’elle stocke. Que vous écriviez une application à niveau unique, à niveau
double ou multiniveau, vous pouvez isoler votre interface utilisateur de la
couche d’accès aux données comme le montre la figure 12.1.

12-8 Guide du développeur


Architecture des bases de données

Figure 12.1 Interface utilisateur des connexions aux ensembles de données dans
toutes applications de base de données

Un formulaire représente l’interface utilisateur et contient les contrôles de


données et d’autres éléments de l’interface utilisateur. Les contrôles de données
d’une interface utilisateur se connectent aux ensembles de données qui
représentent des informations provenant des tables de la base de données. Une
source de données lie les contrôles de données à ces ensembles de données. En
isolant la source de données et les ensembles de données dans un module de
données, le formulaire peut demeurer le même lorsque vous faites évoluer votre
application. Seuls les ensembles de données doivent changer.
Remarque Certains éléments de l’interface utilisateur requièrent une attention particulière
lors de l’anticipation de l’évolutivité. Par exemple, toutes les bases de données
n’ont pas les mêmes impératifs de sécurité. Voir “Sécurité des bases de données”
à la page 12-4 pour plus d’informations sur la gestion de l’authentification des
utilisateurs de façon uniforme lors du changement des bases de données.
Lorsque vous utilisez les composants accès aux données de Delphi (qu’ils
utilisent le BDE, ADO ou InterBase Express), il est facile de passer d’un schéma
à niveau unique à un schéma à niveau double. Seules quelques propriétés de
l’ensemble de données doivent être modifiées pour que l’ensemble de données se
connecte à un serveur SQL et non à une base de données locale.
Une application de bases de données linéaire est facilement adaptable au client
dans une application multiniveau car les deux architectures utilisent le même
composant ensemble de données client. De fait, vous pouvez écrire une
application qui agit à la fois en tant qu’application linéaire et que client
multiniveau (voir “Utilisation du modèle “briefcase”” à la page 13-20).
Vous pouvez écrire une application à niveau unique ou à niveau double dans le
but de la faire passer à terme à une architecture à triple niveau. En plus d’isoler
l’interface utilisateur, isolez toute la logique qui résidera à terme sur le niveau
intermédiaire afin qu’il soit facile de la remplacer. Vous pouvez même connecter
les éléments de votre interface utilisateur aux ensembles de données clients
(utilisés dans les applications multiniveaux) et aux versions locales des ensembles

Conception d’applications de bases de données 12-9


Architecture des bases de données

de données (InterBase, BDE ou ADO) dans un module de données séparé qui


passera à terme au niveau intermédiaire. Si vous ne souhaitez pas introduire
cette possibilité de couche d’ensemble de données supplémentaire dans vos
applications à niveau unique ou à niveau double, vous pouvez facilement passer
à une application à triple niveau ultérieurement. Voir “Passage à une application
à niveau triple” à la page 13-21 pour plus d’informations.

Applications de base de données à niveau unique


Dans les applications de base de données à niveau unique, l’application et la
base de données partagent le même système de fichiers. Elles utilisent des bases
de données locales ou des fichiers qui stockent les informations de la base de
données dans un format linéaire.
Une même application comprend l’interface utilisateur et le mécanisme d’accès
aux données (le moteur de bases de données Borland ou un système de
chargement et d’enregistrement des informations de base de données linéaire). Le
type de composant ensemble de données utilisé pour représenter les tables de la
base de données dépend du support de stockage des données : base de données
locale (comme Paradox, dBASE, Access ou FoxPro) ou linéaire. La figure 12.2
illustre ces deux possibilités.
Figure 12.2 Architectures des applications de base de données à niveau unique

Pour plus d’informations sur la construction des applications de base de données


à niveau unique, voir chapitre 13, “Construction d’applications à niveau unique
et à niveau double”.

Applications de base de données à niveau double


Dans les applications de base de données à niveau double, une application
cliente offre une interface utilisateur aux données et interagit directement avec
un serveur de bases de données distant. La figure 12.3 illustre cette relation.

12-10 Guide du développeur


Architecture des bases de données

Figure 12.3 Architecture des applications de base de données à niveau double

composant base de
éléments source ensemble données
d'interface de données OLE DB
de données distante
utilisateur
ADO

Fiche Module de données

Application Client

composant Moteur
éléments source ensemble base de base de
d'interface de données de données données données
utilisateur BDE Borland distante

Fiche Module de données

Application Client

Dans ce modèle, toutes les applications sont des clients de base de données. Un
client demande des informations à un serveur de bases de données et lui en
envoie. Un serveur peut traiter les requêtes de nombreux clients simultanément,
en coordonnant l’accès aux données et leur mise à jour.
Pour plus d’informations sur la construction d’applications de base de données à
niveau double, voir “Applications basées sur le BDE” à la page 13-2 et “Applications
basées sur ADO” à la page 13-12.

Applications de base de données multiniveaux


Une applications de base de données multiniveau est partitionnée en plusieurs
parties résidant sur différentes machines. L’application client fournit une
interface utilisateur aux données. Elle transmet toutes les requêtes et les mises à
jour de données par l’intermédiaire d’un serveur d’applications (aussi appelé
“broker ou agent des données distantes”). Le serveur d’applications, à son tour,
communique directement avec un serveur de bases de données distant ou un
ensemble de données personnalisé. Dans ce genre de modèle, l’application client,
le serveur d’applications et le serveur de bases de données distant sont en
principe sur des machines séparées. La figure 12.4 (page suivante) illustre ces
relations pour différents types d’applications multiniveaux.

Conception d’applications de bases de données 12-11


Architecture des bases de données

Figure 12.4 Architecture de bases de données multiniveaux

composant fournisseur
connexion
Moteur
éléments base de
source
d'interface composant données base de
de données ensemble
utilisateur ensemble Borland données
de données distante
de données
client
BDE

Fiche Module de données Module de données distant

Application Client Serveur d'application

composant fournisseur
connexion
base de
éléments données
source distante
d'interface composant
de données ensemble
utilisateur ensemble
de données
de données OLE DB
client
ADO

Fiche Module de données Module de données distant

Application Client Serveur d'application

composant fournisseur
connexion

éléments
source
d'interface
de données ensemble ensemble
utilisateur
de données de données
client personnalisé

Fiche Module de données Module de données distant

Application Client Serveur d'application

Delphi peut être utilisé pour créer les applications client et les serveurs
d’applications. Comme l’illustre la figure précédente, dans une application client,
des contrôles orientés données standard connectés à une source de données par
l’intermédiaire d’un ou de plusieurs composants ensemble de données
permettent d’afficher et de modifier des données. Chaque ensemble de données
client communique avec un serveur d’applications par l’intermédiaire d’une
interface IAppServer implémentée par le module de données du serveur
d’applications distant. L’application client peut utiliser une série de protocoles
(TCP/IP, DCOM, MTS, CORBA) pour établir cette communication. Le protocole
dépend du type de composant connexion utilisé dans l’application client et du
type de module de données distant utilisé dans l’application serveur.
Le serveur d’applications inclut des composants fournisseur qui supportent la
communication entre les ensembles de données client de l’application client et les
ensembles de données du serveur d’applications. Toutes les données sont passées
entre l’application client et les composants fournisseur via l’interface IAppServer.

12-12 Guide du développeur


Conception de l’interface utilisateur

Généralement, plusieurs applications client peuvent communiquer avec un seul


serveur d’applications dans un modèle multiniveau. Le serveur d’applications
fournit une passerelle entre vos bases de données et vos applications client, et
vous permet de fournir des tâches de bases de données à l’échelle de
l’entreprise, accessibles à tous les clients et résidant à un emplacement central.
Pour plus d’informations sur la création et l’utilisation d’applications de bases de
données multiniveaux, voir chapitre 14, “Création d’applications multiniveaux”.

Conception de l’interface utilisateur


La page ContrôleBD de la palette des composants offre un ensemble de contrôles
orientés données qui représentent les données de champs d’un enregistrement de
base de données et qui permettent aux utilisateurs de modifier ces données et de
répercuter ces modifications dans la base de données. L’utilisation des contrôles
orientés données vous permet de construire l’interface utilisateur de votre
application de base de données de sorte que ces informations soient visibles et
accessibles aux utilisateurs. Pour plus d’informations sur les contrôles orientés
données, voir chapitre 26, “Utilisation de contrôles de données”.
Les contrôles orientés données reçoivent les données d’un composant source de
données et les lui envoient, (TDataSource ou TIBDataSource). Un composant
source de données agit comme une voie de passage entre l’interface utilisateur et
un composant ensemble de données qui représente un ensemble d’informations
émanant des tables d’une base de données. Plusieurs contrôles orientés données
sur une fiche peuvent partager une même source de données, auquel cas
l’affichage des différents contrôles est synchronisé pour que les valeurs des
champs dont l’utilisateur active l’enregistrement soient affichées dans les
contrôles correspondants. Les composants source de données d’une application
résident généralement dans un module de données, à l’écart des contrôles
orientés données des fiches.
Les contrôles orientés données que vous ajoutez à votre interface utilisateur
dépendent du type de données que vous affichez (texte brut, texte formaté,
graphique, éléments multimédia, etc.). En outre, votre choix des contrôles est
déterminé par la façon dont vous souhaitez organiser les informations et,
éventuellement, par la façon dont vous souhaitez que les utilisateurs puissent
naviguer dans les enregistrements ou les ensembles de données pour y ajouter
ou y modifier des données.
Les sections suivantes présentent les composants que vous pouvez utiliser pour
différents types d’interface utilisateur.

Affichage d’un seul enregistrement


Dans de nombreuses applications, il est souhaitable de n’afficher simultanément que
les informations relatives à un seul enregistrement de données. Par exemple, une
application de saisie de commandes peut afficher les informations relatives à une
seule commande sans indiquer les autres commandes consignées. Ces informations
peuvent provenir d’un seul enregistrement d’un ensemble de commandes.

Conception d’applications de bases de données 12-13


Conception de l’interface utilisateur

Les applications qui affichent un seul enregistrement sont généralement faciles à


lire et à comprendre car toutes les informations de base de données concernent
le même élément (la même commande dans le cas précédent). Les contrôles
orientés données contenus dans ces interfaces utilisateur représentent un seul
champ d’un enregistrement de base de données. La page ContrôleBD de la
palette des composants offre un large choix de contrôles pour la représentation
des différents types de champs. Pour plus d’informations sur tel ou tel contrôle
orienté données, voir “Contrôles représentant un champ unique” à la page 26-9.

Affichage de plusieurs enregistrements


Il se peut que vous souhaitiez afficher de nombreux enregistrements dans la
même fiche. Par exemple, une application de facturation peut afficher sur la
même fiche toutes les commandes passées par un même client.
Pour afficher plusieurs enregistrements, utilisez un contrôle grille. Les contrôles
grille offrent un affichage à champs et enregistrements multiples des données qui
peut rendre l’interface utilisateur de votre application plus attrayante et plus
efficace. Les contrôles grille sont présentés dans “Visualisation et édition des
données avec un contrôle TDBGrid” à la page 26-18 et “Création d’une grille
contenant d’autres contrôles orientés données” à la page 26-31.
Vous pouvez concevoir une interface utilisateur qui affiche à la fois les champs
d’un même enregistrement et les grilles qui représentent plusieurs
enregistrements. Voici deux modèles qui combinent ces deux approches :
• Fiches maître-détail : Vous pouvez représenter les informations à la fois d’une
table maître et d’une table détail en incluant à la fois les contrôles qui
affichent un champ unique et les contrôles grille. Par exemple, vous pouvez
afficher les informations sur un client unique et une grille détail qui affiche les
commandes passées par ce client. Pour plus d’informations sur la liaison de
tables sous-jacentes dans une fiche maître-détail, voir “Création de fiches
maître-détail” à la page 20-27 ou “Utilisation des tables imbriquées” à la
page 20-29.
• Fiches perforation : dans une fiche qui affiche plusieurs enregistrements, vous
pouvez inclure des contrôles champ unique qui affichent les informations
détaillées de l’enregistrement sélectionné. Cette approche est particulièrement
utile lorsque les enregistrements incluent de long mémos ou des informations
graphiques. Lorsque l’utilisateur passe en revue les enregistrements de la
grille, le mémo ou le graphique se met à jour pour représenter la valeur de
l’enregistrement sélectionné. La mise en place de ce dispositif est très facile. La
synchronisation entre les deux affichages est automatique si la grille et le
contrôle mémo ou image partagent une source de données commune.
Conseil Il est généralement préférable de ne pas combiner ces deux approches sur une
même fiche. Alors que le résultat peut s’avérer efficace, les utilisateurs ont
généralement du mal à comprendre la relation entre les données.

12-14 Guide du développeur


Conception de l’interface utilisateur

Analyse des données


Certaines applications de base de données ne présentent pas les informations de
base de données directement à l’utilisateur mais analysent et résument les
informations des bases de données pour permettre aux utilisateurs de tirer des
conclusions à partir des données.
Le composant TDBChart de la page ContrôleBD de la palette des composants
vous permet de présenter les informations de base de données sous forme
graphique afin que les utilisateurs puissent rapidement saisir l’importance des
informations de base de données.
En outre, certaines versions de Delphi proposent une page DecisionCube sur la
palette des composants. Elle contient six composants qui vous permettent
d’analyser les données et de réaliser des références croisées sur les données lors
de la construction d’applications d’aide à la décision. Pour plus d’informations
sur l’utilisation des composants DecisionCube, voir chapitre 27, “Utilisation de
composants d’aide à la décision”.
Si vous souhaitez construire vos propres composants d’affichage de résumés de
données en fonction de divers critères de regroupement, vous pouvez utiliser des
agrégats maintenus avec un ensemble de données client. Pour plus
d’informations sur l’utilisation des agrégats maintenus, voir “Utilisation des
agrégats maintenus” à la page 24-11.

Sélection des données à afficher


Souvent, les données que vous souhaitez dans votre application de base de
données ne correspondent pas exactement aux données d’une seule table de base
de données. Vous pouvez utiliser uniquement un sous-ensemble des champs ou
des enregistrements d’une table. Vous pouvez combiner les informations de
plusieurs tables en une seule vue jointe.
Les données disponibles pour votre application de base de données sont
contrôlées par le composant ensemble de données que vous choisissez. Les
ensembles de données encapsulent les propriétés et les méthodes d’une table de
base de données afin que vous n’ayez pas besoin de réaliser de modifications
majeures selon que les données sont stockées dans une table de base de données
ou dérivées d’une ou plusieurs tables de la base de données. Pour plus
d’informations sur les propriétés et les méthodes courantes des ensembles de
données, voir chapitre 18, “Présentation des ensembles de données”.
Votre application peut contenir plusieurs ensembles de données. Chaque
ensemble de données représente une table logique. L’utilisation des ensembles de
données permet de mettre la logique de votre application en mémoire tampon à
partir de la restructuration des tables physiques de vos bases de données. Il se
peut que vous deviez modifier le type de composant ensemble de données ou la
façon dont il spécifie les données qu’il contient, mais le reste de votre interface
utilisateur demeure fonctionnel sans modification supplémentaire.

Conception d’applications de bases de données 12-15


Conception de l’interface utilisateur

Vous pouvez utiliser l’un quelconque des types d’ensemble de données suivants :
• Composants table (TTable) : les tables correspondent directement aux tables
sous-jacentes de la base de données. Vous pouvez définir quels champs
apparaissent (et même ajouter des champs de référence et des champs
calculés) en utilisant des composants champ persistant. Vous pouvez limiter
les enregistrements qui apparaissent en utilisant des plages ou des filtres. Les
tables sont décrites de façon plus détaillée dans le chapitre 20, “Manipulation
des tables”. Les champs persistants sont décrits dans “Champs persistants” à
la page 19-4. Les plages et les filtres sont décrits dans “Manipulation d’un
sous-ensemble de données” à la page 20-12.
• Composants requête (TQuery) : les requêtes offrent le mécanisme le plus
général pour spécifier le contenu d’un ensemble de données basé sur le BDE.
Vous pouvez combiner les données de plusieurs tables en utilisant des
jointures et limiter les champs et les enregistrements qui apparaissent en
fonction de critères exprimés en langage SQL. Pour plus d’informations sur les
requêtes, voir chapitre 21, “Manipulation des requêtes”.
• Procédures stockées (TStoredProc) : les procédures stockées sont des
ensembles d’instructions SQL qui sont nommées et stockées sur un serveur
SQL. Si votre serveur de bases de données définit une procédure distante qui
renvoie l’ensemble de données que vous souhaitez, vous pouvez utiliser un
composant procédure stockée. Pour plus d’informations sur les procédures
stockées, voir chapitre 22, “Manipulation des procédures stockées”.
• Ensembles de données imbriqués (TNestedTable) : les ensembles de données
imbriqués représentent les enregistrements d’un ensemble détail Oracle8
imbriqué. Delphi ne vous permet pas de créer des tables Oracle8 contenant
des champs ensemble de données imbriqués, mais vous pouvez modifier et
afficher les données provenant de champs ensemble de données existants à
l’aide d’ensembles de données imbriqués. L’ensemble de données imbriqué
obtient ses données à partir d’un composant champ ensemble de données
d’un ensemble de données contenant des données Oracle8. Voir “Utilisation
des tables imbriquées” à la page 20-29 et “Utilisation des champs ensemble de
données” à la page 19-30 pour plus d’informations sur la représentation des
champs ensemble de données à l’aide des ensembles de données imbriqués.
Quand vous utilisez ADO pour accéder à vos données, vous pouvez utiliser l’un
quelconque des types d’ensembles de données suivants :
• Ensembles de données ADO (TADODataset) : les ensembles de données
ADO offrent le mécanisme le plus souple pour accéder aux données en
utilisant ADO. Les ensembles de données ADO peuvent représenter une seule
table de base de données ou les résultats d’une requête SQL. Vous pouvez
définir quels champs apparaissent (et même ajouter des champs de référence
et des champs calculés) en utilisant des composants champ persistant. Vous
pouvez limiter les enregistrements qui apparaissent en utilisant des plages ou
des filtres. Vous pouvez spécifier une instruction SQL qui génère les données.
Les ensembles de données ADO sont décrits en détail dans “Caractéristiques
communes à tous les composants ensemble de données ADO” à la page 23-13
et “Utilisation de TADODataSet” à la page 23-20.

12-16 Guide du développeur


Conception de l’interface utilisateur

• Composants table ADO (TADOTable) : les tables ADO correspondent


directement aux tables sous-jacentes de la base de données. Vous pouvez
définir quels champs apparaissent (et même ajouter des champs de référence
et des champs calculés) en utilisant des composants champ persistant. Vous
pouvez limiter les enregistrements qui apparaissent en utilisant des plages ou
des filtres. Les tables ADO sont décrites en détail dans “Utilisation de
TADOTable” à la page 23-21.
• Composants requête ADO (TADOQuery) : les requêtes ADO représentent
l’ensemble de résultats à partir duquel exécuter une commande SQL ou une
instruction en langage de définition des données (DDL). Pour plus
d’informations sur les requêtes ADO, voir “Utilisation de TADOQuery” à la
page 23-22.
• Procédures stockées ADO (TADOStoredProc ) : si votre serveur de bases de
données définit une procédure stockée qui renvoie l’ensemble de données que
vous voulez, vous pouvez utiliser un composant procédure stockée ADO.
Pour plus d’informations sur les procédures stockées ADO, voir “Utilisation
de TADOStoredProc” à la page 23-24.
Si vous utilisez InterBase comme serveur de bases de données, vous pouvez
utiliser l’un quelconque des types d’ensembles de données suivants :
• Ensembles de données IB (TIBDataSet) : les ensembles de données IB
représentent l’ensemble de résultats d’une instruction SQL (habituellement une
instruction SELECT). Vous pouvez spécifier des instructions SQL pour
sélectionner et mettre à jour les données mises en tampon par l’ensemble de
données.
• Composants table IB (TIBTable) :les tables IB obtiennent leurs données
directement d’une table ou d’une vue InterBase. Vous pouvez définir quels
champs apparaissent (et même ajouter des champs de référence et des champs
calculés) en utilisant des composants champ persistant. Vous pouvez limiter
les enregistrements qui apparaissent en utilisant des filtres.
• Composants requête IB (TIBQuery) : les requêtes IB représentent l’ensemble
de résultats à partir duquel exécuter une commande SQL. Les requêtes IB sont
les composants ensemble de données qui permettent de passer le plus
facilement d’InterBase local à un serveur InterBase distant.
• Procédures stockées IB (TIBStoredProc) : IBStoredProc exécute une procédure
stockée InterBase Execute. Ces ensembles de données ne renvoient pas
d’ensemble de résultats : pour les procédures stockées qui renvoient un
ensemble de résultats, vous devez utiliser TIBDataSet ou TIBQuery.

Conception d’applications de bases de données 12-17


Conception de l’interface utilisateur

Si vous n’utilisez ni le BDE, ni ADO, ni InterBase, Delphi propose les options


suivantes :
• Ensembles de données client (TClientDataSet) : les ensembles de données client
placent en mémoire cache les enregistrements d’ensembles de données logiques.
Ainsi, ils ne peuvent contenir qu’un nombre limité d’enregistrements. Les
ensembles de données client sont remplis de données de deux façons : à partir
d’un serveur d’applications ou de données linéaires stockées sur disque. Ils ne
nécessitent pas de moteur de base de données tel que BDE ou ADO, mais une
seule DLL (Midas.dll). Pour plus d’informations sur les ensembles de données
client, voir chapitre 24, “Création et utilisation d’un ensemble de données client”.
• Ensembles de données personnalisés : vous pouvez créer vos propres
descendants personnalisés de TDataSet pour représenter un corps de données
que vous créez ou auquel vous accédez par du code que vous écrivez.
L’écriture d’ensembles de données personnalisés vous permet de gérer les
données avec la méthode de votre choix et d’utiliser parallèlement les
contrôles données de la VCL pour construire votre interface utilisateur. Pour
plus d’informations sur la création de composants personnalisés, voir
chapitre 31, “Présentation générale de la création d’un composant”.

Ecriture de rapports
Si vous souhaitez que les utilisateurs puissent imprimer les informations de base
de données émanant des ensembles de données de votre application, vous
pouvez utiliser les composants rapport de la page QReport de la palette des
composants. L’utilisation de ces composants vous permet de construire
visuellement des rapports à bandes pour présenter et résumer les informations
contenues dans vos tables de base de données. Vous pouvez ajouter des résumés
aux en-têtes et aux pieds de page de groupe pour analyser les données en
fonction de critères de regroupement.
Démarrez un rapport pour votre application en sélectionnant l’icône QuickReport
dans la boîte de dialogue Nouveaux éléments. Sélectionnez Fichier|Nouveau
dans le menu principal et allez à la page Affaires. Double-cliquez sur l’icône
Expert QuickReport pour lancer l’expert.
Remarque Pour un exemple d’utilisation des composants de la page QReport, reportez-vous
au programme exemple QuickReport livré avec Delphi.

12-18 Guide du développeur


Chapitre

Construction d’applications à niveau


Chapter 13
13
unique et à niveau double
Les applications à niveau unique et à niveau double comprennent la logique qui
manipule les informations de base de données et assurent l’implémentation de
l’interface utilisateur. Comme la logique de manipulation des données n’est pas
placée dans un niveau séparé, ces types d’applications sont intéressants
lorsqu’aucune autre application ne partage les mêmes informations de base de
données. Même si d’autres applications partagent les informations de base de
données, ces types d’applications sont intéressants si la base de données est très
simple et qu’aucune sémantique de données ne doit être reproduite par les
applications qui utilisent les données.
Vous pouvez commencer par l’écriture d’une application à niveau unique ou à
niveau double même si vous envisagez de passer à terme à un modèle
multiniveau pour répondre à vos besoins. Cette approche vous évite de
développer une logique de manipulation de données immédiatement et permet
au serveur d’applications d’être disponible pendant que vous écrivez l’interface
utilisateur. Elle vous permet également de développer un prototype simple et
peu onéreux avant d’investir dans un projet de développement multisystème. Si
vous envisagez de passer à terme à une application multiniveau, vous pouvez
isoler la logique de manipulation de données afin qu’il soit facile de la déplacer
dans un niveau intermédiaire ultérieurement.
Delphi gère deux types d’applications à niveau unique : les applications utilisant
une base de données locale (comme Paradox, dBase, Access ou Interbase Local)
et les applications de base de données linéaires. Les applications à deux niveaux
utilisent un pilote pour accéder à une base de données distante.

Construction d’applications à niveau unique et à niveau double 13-1


Applications basées sur le BDE

Les considérations intervenant dans le choix entre une application à un seul


niveau utilisant une base de données locale ou des applications à deux niveaux
sont quasiment les mêmes et dépendent avant tout du mécanisme que vous
choisissez d’adopter pour vous connecter à la base de données. Delphi propose
trois mécanismes intégrés différents pour ce type d’applications :
• Les applications utilisant le BDE
• Les applications utilisant ADO
• Les applications InterBaseExpress
Les applications utilisant des bases de données linéaires utilisent la gestion
d’ensembles de données client proposée par MIDAS.DLL.

Applications basées sur le BDE


Comme les composants d’accès aux données (et le moteur de bases de données
Borland) gèrent les détails de la lecture, de la mise à jour et de la navigation dans
les données, l’écriture d’applications à niveau double basées sur le BDE est dans
son essence identique à celle d’applications à niveau unique basées sur le BDE.
Lorsque vous déployez des applications basées sur le BDE, vous devez inclure le
BDE avec votre application. Bien que cela accroisse la taille de l’application et la
complexité du déploiement, le BDE peut être partagé avec d’autres applications
basées sur le BDE et offre de nombreux avantages. Les applications basées sur le
BDE vous permettent d’utiliser la puissante bibliothèque des appels de l’interface
de programmation d’applications du moteur de bases de données Borland.
Même si vous ne souhaitez pas utiliser l’interface de programmation
d’applications du BDE, l’écriture d’applications basées sur le BDE vous offre les
caractéristiques suivantes, non disponibles pour les autres applications telles que
les applications de base de données linéaires :
• Connexion aux bases de données.
• Utilisation des transactions.
• Mise en mémoire cache des mises à jour.
• Création et restructuration des tables de base de données.

Architecture basée sur le BDE


Une application à niveau unique ou à niveau double basée sur le BDE comprend :
• une interface utilisateur contenant des contrôles orientés données ;
• un ou plusieurs ensembles de données qui représentent les informations des
tables de base de données ;
• un composant source de données pour chaque ensemble de données pour
connecter les contrôles orientés données aux ensembles de données.
• éventuellement, un ou plusieurs composants base de données pour contrôler les
transactions dans les applications à niveau unique et à niveau double et pour
gérer les connexions aux bases de données dans les applications à niveau double ;

13-2 Guide du développeur


Applications basées sur le BDE

• éventuellement, un ou plusieurs composants session pour isoler les opérations


d’accès aux données, telles que les connexions aux bases de données, et pour
gérer les groupes de bases de données.
La relation entre ces éléments est illustrée dans la figure 13.1 :
Figure 13.1 Composants d’une application basée sur le BDE

Présentation des bases de données et des ensembles de données


Les bases de données contiennent des informations stockées dans des tables.
Elles peuvent aussi comprendre des tables d’informations sur le contenu de la
base de données, des objets tels que des index utilisés par les tables et des objets
SQL tels que les procédures stockées. Voir chapitre 17, “Connexion aux bases de
données” pour plus d’informations sur les bases de données.
La page AccèsBD de la palette des composants comprend divers composants
ensemble de données qui représentent les tables contenues dans une base de
données ou les tables logiques construites à partir de données stockées dans ces
tables de base de données. Voir “Sélection des données à afficher” à la
page 12-15 pour plus d’informations sur ces composants ensemble de données.
Vous devez inclure un composant ensemble de données dans votre application
pour manipuler les informations de base de données.
Chaque composant ensemble de données activé par le BDE de la page Accès BD
dispose d’une propriété DatabaseName publiée qui spécifie la base de données qui
contient la ou les tables renfermant les informations de cet ensemble de données.
Lorsque vous définissez votre application, vous devez utiliser cette propriété
pour spécifier la base de données afin de pouvoir lier l’ensemble de données aux
informations spécifiques contenues dans cette base de données. La valeur que
vous spécifiez dépend des éléments suivants :
• La base de données possède un alias BDE. Vous pouvez spécifier un alias
BDE comme valeur de DatabaseName. Un alias BDE représente une base de
données ainsi que les informations de configuration de cette base de données.
Les informations de configuration associées à l’alias diffèrent suivant le type
de base de données (Oracle, Sybase, InterBase, Paradox, dBASE, etc.). Utilisez
l’outil d’administration du BDE ou l’explorateur SQL pour créer et gérer des
alias BDE.

Construction d’applications à niveau unique et à niveau double 13-3


Applications basées sur le BDE

• La base de données est une base de données Paradox ou dBASE. Si vous


utilisez une base de données Paradox ou dBASE, DatabaseName peut spécifier
le répertoire dans lequel se trouvent les tables de base de données.
• Vous utilisez des composants base de données explicites. Les composants base
de données (TDatabase) représentent une base de données dans votre
application. Si vous n’ajoutez pas de composant base de données de façon
explicite, un composant temporaire est créé automatiquement en fonction de la
valeur de la propriété DatabaseName. Si vous utilisez des composants base de
données explicites, DatabaseName est la valeur de la propriété DatabaseName du
composant base de données. Voir “Présentation des composants base de
données persistants et temporaires” à la page 17-1 pour plus d’informations
sur l’utilisation des composants base de données.

Utilisation des sessions


Les sessions isolent les opérations d’accès aux données, comme les connexions aux
bases de données, et gèrent les groupes de bases de données. L’entière utilisation
du moteur de bases de données Borland a lieu dans le contexte d’une session. Vous
pouvez utiliser les sessions pour spécifier des informations de configuration qui
s’appliquent à toutes les bases de données dans la session. Cela vous permet de
redéfinir le comportement par défaut spécifié avec l’outil d’administration du BDE.
Vous pouvez utiliser une session pour :
• gérer les alias BDE. Vous pouvez créer de nouveaux alias, supprimer des alias
ou modifier les alias existants. Par défaut, les modifications n’affectent que la
session mais vous pouvez écrire des modifications pour qu’elles soient ajoutées
au fichier permanent de configuration du BDE. Pour plus d’informations sur la
gestion des alias BDE, voir “Manipulation des alias BDE” à la page 16-11.
• contrôler à quel moment les connexions aux bases de données sont fermées
dans les applications à niveau double. Le fait de maintenir ouvertes les
connexions aux bases de données alors qu’aucun ensemble de données de la
base de données n’est actif monopolise des ressources qui pourraient être
libérées mais améliore la vitesse et réduit le trafic réseau. Pour que les
connexions aux bases de données demeurent ouvertes même lorsqu’aucun
ensemble de données n’est actif, il faut que la propriété KeepConnections ait
pour valeur True (valeur par défaut).
• gérer l’accès aux fichiers Paradox et dBASE protégés par mot de passe dans les
applications à niveau unique. Les ensembles de données qui accèdent aux
tables Paradox et dBASE protégées par mot de passe utilisent le composant
session pour fournir un mot de passe lorsque ces tables doivent être ouvertes.
Vous pouvez passer outre le comportement par défaut (apparition d’une boîte
de dialogue de mot de passe chaque fois qu’un mot de passe est requis) et
fournir les mots de passe par programmation. Si vous envisagez de transformer
votre application à niveau unique en une application à niveau double ou
multiniveau, vous pouvez créer une interface utilisateur commune pour
l’obtention des informations d’authentification de l’utilisateur qui demeure la
même lorsque vous utilisez des serveurs de bases de données distants qui
requièrent un nom d’utilisateur et un mot de passe au niveau du serveur et

13-4 Guide du développeur


Applications basées sur le BDE

non au niveau de la table. Pour plus d’informations sur l’utilisation des


sessions pour gérer les mots de passe Paradox et dBASE, voir “Manipulation de
tables Paradox et dBase protégées par mot de passe” à la page 16-15.
• spécifier l’emplacement de répertoires Paradox particuliers. Les bases de
données Paradox partagées sur un réseau utilisent un répertoire réseau qui
contient les fichiers temporaires qui spécifient les informations de verrouillage
des tables et des enregistrements. Les bases de données Paradox utilisent aussi
un répertoire privé dans lequel sont conservés les fichiers temporaires, tels
que les résultats de requêtes. Pour plus d’informations sur la spécification des
emplacements de ces répertoires, voir “Spécification de l’emplacement des
répertoires Paradox” à la page 16-14 .
Si votre application peut accéder à la même base de données à plusieurs reprises
simultanément, vous devez utiliser plusieurs sessions pour isoler chaque
utilisation de la base de données. Si vous n’y parvenez pas, la logique qui
gouverne les transactions sur cette base de données est interrompue (notamment
les transactions créées automatiquement). Les applications sont exposées à des
accès simultanés lors de l’exécution simultanée de requêtes ou de l’utilisation de
plusieurs threads. Pour plus d’informations sur l’utilisation de plusieurs sessions,
voir “Gestion de plusieurs sessions” à la page 16-17.
Sauf si vous devez utiliser plusieurs sessions, vous pouvez utiliser la session par
défaut.

Connexion aux bases de données


Le moteur de bases de données Borland comprend des pilotes qui permettent de
se connecter à diverses bases de données. La version standard de Delphi n’inclut
que les pilotes destinés aux bases de données locales : Paradox, dBASE, FoxPro
et Access. La version Professionnelle offre un adaptateur ODBC grâce auquel le
BDE peut utiliser les pilotes ODBC. Un pilote ODBC permet à votre application
d’utiliser n’importe quelle base de données de type ODBC. Certaines versions
incluent également des pilotes destinés aux serveurs de bases de données
distants. Utilisez les pilotes installés avec SQL Links pour communiquer avec les
serveurs de bases de données distants tels que InterBase, Oracle, Sybase,
Informix, Microsoft SQL server et DB2.
Remarque La seule différence entre une application à niveau unique basée sur le BDE et
une application à niveau double basée sur le BDE réside dans l’utilisation de
bases de données locales ou de serveurs de bases de données distants.

Utilisation des transactions


Une transaction est un groupe d’actions devant être menées avec succès sur une
ou plusieurs tables d’une base de données avant d’être validées (rendues
définitives). Si l’une des actions du groupe échoue, toutes les actions sont
annulées (abandonnées). Les transactions préservent l’homogénéité de la base de
données en cas d’occurrence d’un problème suite à l’exécution d’une des actions
qui les composent.

Construction d’applications à niveau unique et à niveau double 13-5


Applications basées sur le BDE

Par exemple, dans une application bancaire, le transfert de fonds d’un compte
vers un autre est une opération qui mérite d’être protégée avec une transaction.
Si, après diminution du solde d’un compte, une erreur se produit dans
l’augmentation du solde de l’autre compte, il est souhaitable d’annuler la
transaction afin que la base de données continue de refléter le solde total correct.
Par défaut, le BDE permet de contrôler explicitement les transactions de vos
applications. Lorsqu’une application est placée sous contrôle implicite des
transactions, une transaction séparée est utilisée pour chaque enregistrement de
l’ensemble de données écrit dans la base de données sous-jacente. Les
transactions implicites réduisent au minimum les conflits de mise à jour des
enregistrements et garantissent l’homogénéité de la vue de la base de données.
D’un autre côté, comme chaque ligne de données écrite dans une base de
données occupe de la place dans sa propre transaction, le contrôle des
transactions implicites peut générer un trafic réseau excessif et ralentir la
performance de l’application. De plus, le contrôle des transactions implicites ne
protège pas les opérations logiques qui englobent plusieurs enregistrements,
comme le transfert de fonds précédemment décrit.
Si vous contrôlez les transactions de façon explicite, vous pouvez efficacement
décider à quel moment les démarrer, les valider et les annuler. Lorsque vous
développez des applications dans un environnement multi-utilisateur, notamment
lorsque vos applications utilisent un serveur SQL distant, vous devez contrôler
les transactions de façon explicite.
Remarque Vous pouvez réduire au minimum le nombre de transactions requises en mettant
les mises à jour en mémoire cache. Pour plus d’informations sur les mises à jour
mises en mémoire cache, voir chapitre 25, “Manipulation des mises à jour en
mémoire cache”

Contrôle explicite des transactions


Deux méthodes mutuellement exclusives permettent de contrôler les transactions
de façon explicite dans une application de base de données basée sur le BDE :
• Utiliser les méthodes et les propriétés du composant base de données, comme
StartTransaction, Commit, Rollback, InTransaction et TransIsolation. L’avantage
principal de l’utilisation des méthodes et des propriétés d’un composant base
de données pour contrôler les transactions est que cela offre une application
propre et portable indépendante de toute base de données ou de tout serveur.
• Utiliser le SQL direct dans un composant requête pour transmettre les
instructions SQL directement aux serveurs SQL ou ODBC distants. Pour plus
d’informations sur les composants requête, voir chapitre 21, “Manipulation des
requêtes” L’avantage principal de SQL direct est que vous pouvez utiliser les
fonctionnalités avancées de gestion de transactions d’un serveur de bases de
données particulier, comme la mise en cache des schémas. Pour comprendre
les avantages du modèle de gestion de transactions de votre serveur de bases
de données, reportez-vous à sa documentation.

13-6 Guide du développeur


Applications basées sur le BDE

Les applications à niveau unique ne peuvent pas utiliser SQL direct. Vous
pouvez utiliser le composant base de données pour créer des transactions
explicites pour les bases de données locales. Toutefois, il y a certains limites à
l’utilisation des transactions locales. Pour plus d’informations sur l’utilisation des
transactions locales, voir “Utilisation des transactions locales” à la page 13-10.
Lorsque vous écrivez des applications à niveau double (qui requièrent SQL
Links), vous pouvez utiliser un composant base de données ou SQL direct pour
gérer les transactions. Pour plus d’informations sur l’utilisation de SQL direct,
voir “Utilisation du SQL direct” à la page 13-9.

Utilisation d’un composant base de données pour les transactions


Lorsque vous démarrez une transaction, toutes les instructions suivantes qui
réalisent des opérations de lecture ou d’écriture sur la base de données
interviennent dans le contexte de cette transaction. Chaque instruction est
considérée comme élément d’un groupe. Les modifications apportées à la base de
données doivent être correctement validées, sinon toutes les modifications
réalisées dans le groupe doivent être annulées.
Au mieux, une transaction ne doit durer que le temps nécessaire. Plus une
transaction demeure active, plus d’utilisateurs peuvent simultanément accéder à la
base de données. De même, plus nombreuses sont les transactions qui démarrent
et s’achèvent simultanément pendant la durée de vie de votre transaction, plus
grande est la probabilité pour que votre transaction entre en conflit avec une autre
transaction lorsque vous essayez de valider vos modifications.
Lorsque vous utilisez un composant base de données, vous codez une seule
transaction comme suit :
1 Démarrez la transaction en appelant la méthode StartTransaction de la base de
données:
DatabaseInterBase.StartTransaction;
2 Une fois que la transaction est lancée, toutes les actions de base de données
suivantes sont considérées comme éléments de la transaction jusqu’à ce que la
transaction soit achevée de façon explicite. Vous pouvez déterminer si une
transaction est en cours en contrôlant la propriété InTransaction du composant
base de données. Pendant que la transaction est en cours, ce que vous pouvez
voir des données contenues dans les tables de base de données est déterminé
par votre niveau d’isolement des transactions. Pour plus d’informations sur les
niveaux d’isolement des transactions, voir “Utilisation de la propriété
TransIsolation” à la page 13-8.
3 Lorsque les actions qui composent la transaction ont toutes réussi, vous
pouvez rendre définitives les modifications de la base de données en utilisant
la méthode Commit du composant base de données:
DatabaseInterBase.Commit;
Commit est généralement tentée dans une instruction try...except. Ainsi, si une
transaction ne peut pas être correctement validée, vous pouvez utiliser le bloc
except pour traiter l’erreur et renouveler l’opération ou pour annuler la
transaction.

Construction d’applications à niveau unique et à niveau double 13-7


Applications basées sur le BDE

4 Si une erreur se produit pendant l’application des modifications faisant partie


de la transaction ou pendant la tentative de validation de la transaction, vous
pouvez supprimer toutes les modifications qui composent la transaction. Pour
supprimer ces modifications, utilisez la méthode Rollback du composant base
de données:
DatabaseInterBase.Rollback;
Rollback est généralement exécutée
• Dans un code de gestion des exceptions, lorsque vous ne pouvez pas
rétablir la situation après une erreur de base de données.
• Dans un code d’événement de bouton ou de menu, comme lorsqu’un
utilisateur clique sur un bouton Annuler.

Utilisation de la propriété TransIsolation


TransIsolation définit le niveau d’isolement pour les transactions d’un composant
base de données. Le niveau d’isolement des transactions détermine comment une
transaction interagit avec d’autres transactions simultanées lorsque ces
transactions manipulent les mêmes tables. Il affecte, en particulier, ce qu’une
transaction “voit” des changements apportés à une table par les autres
transactions.
La valeur par défaut de TransIsolation est tiReadCommitted. Le tableau ci-dessous
présente les valeurs que peut prendre TransIsolation et donne leur signification :

Tableau 13.1 Valeurs possibles pour la propriété TransIsolation


Niveau d’isolement Signification
tiDirtyRead Autorise la lecture des changements non validés apportés à la base de
données par d’autres transactions simultanées. Les changements non
validés ne sont pas permanents et peuvent être annulés à tout moment.
C’est le niveau où votre transaction est la moins isolée des
changements apportés par d’autres transactions.
tiReadCommitted Autorise uniquement la lecture des changements validés (permanents)
apportés à la base de données par d’autres transactions simultanées. Il
s’agit du niveau d’isolement par défaut.
tiRepeatableRead Permet d’avoir une vue unique de la base de données lue. Votre
transaction ne peut pas voir les changements ultérieurs apportés par
d’autres transactions simultanées. Ce niveau d’isolement garantit
qu’après la lecture d’un enregistrement par la transaction, la vue de cet
enregistrement ne changera pas. C’est le niveau où votre transaction est
la mieux isolée des changements apportés par d’autres transactions.

Les serveurs de bases de données peuvent supporter ces niveaux d’isolement de


différentes manières ou ne pas les supporter du tout. Si le niveau d’isolement
demandé n’est pas supporté par le serveur, le BDE utilise le niveau d’isolement
immédiatement supérieur. Le niveau d’isolement réellement utilisé par certains
serveurs est présenté dans le tableau 13.2, “Niveaux d’isolement des transactions

13-8 Guide du développeur


Applications basées sur le BDE

des différents serveurs.” Pour une description détaillée de la mise en œuvre de


chaque niveau d’isolement, reportez-vous à la documentation de votre serveur.

Tableau 13.2 Niveaux d’isolement des transactions des différents serveurs


Serveur Niveau spécifié Niveau réel
Oracle tiDirtyRead tiReadCommitted
tiReadCommitted tiReadCommitted
tiRepeatableRead tiRepeatableRead (READONLY)
Sybase, MS-SQL tiDirtyRead tiReadCommitted
tiReadCommitted tiReadCommitted
tiRepeatableRead Non supporté
DB2 tiDirtyRead tiDirtyRead
tiReadCommitted tiReadCommitted
tiRepeatableRead tiRepeatableRead
Informix tiDirtyRead tiDirtyRead
tiReadCommitted tiReadCommitted
tiRepeatableRead tiRepeatableRead
InterBase tiDirtyRead tiReadCommitted
tiReadCommitted tiReadCommitted
tiRepeatableRead tiRepeatableRead
Paradox, dBASE, tiDirtyRead tiDirtyRead
Access, FoxPro tiReadCommitted Non supporté
tiRepeatableRead Non supporté

Remarque Lorsque vous utilisez des transactions avec des tables locales Paradox, dBASE,
Access et FoxPro, mettez TransIsolation à tiDirtyRead plutôt que d’utiliser la
valeur par défaut de tiReadCommitted. Une erreur BDE est renvoyée si
TransIsolation a une valeur autre que tiDirtyRead pour les tables locales.
Lorsqu’une application utilise ODBC pour s’interfacer avec un serveur, il faut
que le pilote ODBC supporte aussi le niveau d’isolement voulu. Pour plus
d’informations, reportez-vous à la documentation de votre pilote ODBC.

Utilisation du SQL direct


Avec SQL direct, vous pouvez utiliser un composant TQuery, TStoredProc ou
TUpdateSQL pour envoyer directement une instruction SQL de contrôle des
transactions à un serveur de bases de données distant. Le BDE ne traite pas
l’instruction SQL. L’utilisation du SQL direct vous permet de tirer parti des
possibilités de contrôle de transactions offertes par votre serveur, notamment si
ces contrôles ne sont pas standard.
Pour contrôler une transaction à l’aide du SQL direct, il faut
• Installer les pilotes SQL Links corrects. Si vous avez choisi l’installation
“Standard” de Delphi, tous les pilotes SQL Links sont déjà correctement installés.
• Configurer correctement le protocole réseau. Pour plus d’informations,
reportez-vous à la documentation d’administration de votre réseau.
• Pouvoir accéder à une base de données sur un serveur distant.

Construction d’applications à niveau unique et à niveau double 13-9


Applications basées sur le BDE

• Définir SQLPASSTHRU MODE à NOT SHARED avec l’explorateur SQL.


SQLPASSTHRU MODE indique si le BDE et les instructions SQL direct
peuvent partager les mêmes connexions de base de données. SQLPASSTHRU
MODE est généralement défini à SHARED AUTOCOMMIT. Toutefois, vous ne
pouvez pas partager les connexions de base de données lorsque vous utilisez
des instructions de contrôle de transaction. Pour plus d’informations sur les
modes SQLPASSTHRU, voir l’aide en ligne de l’administrateur BDE.
Remarque Quand SQLPASSTHRU MODE est configuré à NOT SHARED, il faut utiliser des
composants base de données distincts pour les ensembles de données qui
transmettent des instructions de transactions SQL au serveur et pour les autres
ensembles de données.

Utilisation des transactions locales


Le BDE supporte les transactions sur des tables Paradox, dBASE, Access et
FoxPro. Au niveau programmation, il n’y a aucune différence entre une
transaction locale et une transaction sur un serveur de bases de données distant.
Quand une transaction est démarrée sur une table locale, les mises à jour
effectuées sur la table sont enregistrées dans un journal. Chaque enregistrement
du journal contient la zone tampon des précédentes valeurs d’un enregistrement.
Quand une transaction est active, les enregistrements mis à jour sont verrouillés
jusqu’à ce que la transaction soit validée ou annulée. En cas d’annulation, les
zones tampon des précédentes valeurs sont réaffectées aux enregistrements mis à
jour afin de restaurer les valeurs d’origine.
Les transactions locales sont plus limitées que les transactions sur les serveurs
SQL ou les pilotes ODBC. Les limitations suivantes s’appliquent notamment sur
les transactions locales :
• Il n’y a pas de récupération automatique en cas d’arrêt imprévu.
• Les instructions de définition des données ne sont pas supportées.
• Il n’est pas possible d’exécuter des transactions sur des tables temporaires.
• Pour Paradox, les transactions locales ne peuvent être exécutées que sur des
tables avec des index valides. Les données des tables Paradox non indexées ne
peuvent pas être annulées.
• Seul un nombre limité d’enregistrements peuvent être verrouillés et modifiés.
Pour les tables Paradox, vous êtes limité à 255 enregistrements. Pour dBASE,
vous êtes limité à 100 enregistrements.
• Il n’est pas possible d’exécuter des transactions avec le pilote ASCII du BDE.
• Le niveau TransIsolation doit être défini à tiDirtyRead.
• Durant une transaction, la fermeture d’un curseur sur une table annule
toujours celle-ci, sauf dans les cas suivants :
• Si plusieurs tables sont ouvertes.
• Si le curseur est fermé sur une table n’ayant fait l’objet d’aucun
changement.

13-10 Guide du développeur


Applications basées sur le BDE

Mise en mémoire cache des mises à jour


Le moteur de bases de données Borland permet de mettre les mises à jour en
mémoire cache. Lorsque vous mettez les mises à jour en mémoire cache, votre
application extrait les données d’une base de données, apporte toutes les
modifications à une copie des données locale et mise en mémoire cache, puis
applique les modifications mises en mémoire cache à l’ensemble de données. Les
mises à jour mises en mémoire cache sont appliquées à la base de données en
une seule transaction.
La mise en mémoire cache des mises à jour réduit au minimum la durée des
transactions et allège le trafic réseau. Toutefois, les données mises en mémoire
cache sont des données locales vis-à-vis de votre application et ne sont pas sous
le contrôle des transactions. Cela signifie que, pendant que vous travaillez sur
votre copie des données locale mise en mémoire cache, les autres applications
peuvent modifier les données dans la table de base de données sous-jacente. De
plus, elles n’ont conscience de vos modifications que lorsque vous appliquez les
mises à jour mises en mémoire cache. C’est pourquoi les mises à jour mises en
mémoire cache peuvent ne pas être appropriées pour les applications manipulant
des données volatiles, car vous pouvez créer ou rencontrer de trop nombreux
conflits lorsque vous essayez d’ajouter vos modifications dans la base de données.
Vous pouvez indiquer aux ensembles de données activés par le BDE de mettre
les mises à jour en mémoire cache à l’aide de la propriété CachedUpdates. Lorsque
les modifications sont achevées, elles peuvent être appliquées par le composant
ensemble de données, par le composant base de données ou par un objet de
mise à jour particulier. Lorsque les modifications ne peuvent pas être appliquées
à la base de données sans traitement supplémentaire (par exemple, lorsque vous
utilisez une requête jointe), vous devez utiliser l’événement OnUpdateRecord pour
écrire les modifications dans chaque table qui fait partie de la vue jointe.
Pour plus d’informations sur la mise en mémoire cache des mises à jour, voir
chapitre 25, “Manipulation des mises à jour en mémoire cache”.
Remarque Si vous utilisez le cache des mises à jour, vous pouvez envisager la possibilité de
passer à un modèle à plusieurs niveaux afin d’avoir un meilleur contrôle des
mises à jour dans l’application. Pour davantage d’informations sur le modèle
multiniveau, voir chapitre 14, “Création d’applications multiniveaux”.

Création et restructuration des tables de base de données


Dans les applications basées sur le BDE, vous pouvez utiliser le composant
TTable pour créer de nouvelles tables de base de données et pour ajouter des
index aux tables existantes.
Vous pouvez créer des tables en mode conception, dans le concepteur de fiches,
ou en mode exécution. Pour créer une table, vous devez spécifier les champs de
la table à l’aide de la propriété FieldDefs, ajouter tous les index à l’aide de la
propriété IndexDefs et appeler la méthode CreateTable (ou appeler la commande
Créer une table dans le menu contextuel de la table). Pour plus de détails sur la
création des tables, voir “Création d’une table” à la page 20-19.

Construction d’applications à niveau unique et à niveau double 13-11


Applications basées sur ADO

Remarque Lorsque vous créez des tables Oracle8, vous ne pouvez pas créer de champs
objet (champs ADT, champs tableau, champs de référence et champs ensemble
de données).
Si vous souhaitez restructurer une table en mode exécution (autrement qu’en
ajoutant des index), vous devez utiliser l’API du BDE DbiDoRestructure. Vous
pouvez ajouter des index à toute table existante à l’aide de la méthode AddIndex
de TTable.
Remarque En mode conception, vous pouvez utiliser le Module base de données pour créer
et restructurer des tables Paradox et dBASE. Pour créer et restructurer des tables
sur des serveurs distants, utilisez l’explorateur SQL et restructurez les tables à
l’aide de SQL.

Applications basées sur ADO


Les applications Delphi utilisant les composants ADO pour accéder aux données
peuvent utiliser un ou deux niveaux. La catégorie de l’application dépend du
type de base de données utilisée. Une application utilisant ADO pour accéder à
une base de données Microsoft SQL Server est toujours à deux niveaux car SQL
Server est un système de bases de données SQL. Les systèmes de bases de
données SQL sont généralement placés sur un serveur SQL dédié. Par contre,
une application utilisant ADO pour accéder à des bases de données locales
comme dBASE ou FoxPro est toujours une application à un niveau.
L’accès à des bases de données en utilisant ADO et les composants ADO Delphi
présente quatre aspects. Ces aspects sont les mêmes que l’application ait un ou
deux niveaux. Ce sont :
• L’architecture basée sur ADO
• La connexion à des bases de données ADO
• L’accès aux données
• La création et la restructuration de tables de bases de données ADO

Architecture basée sur ADO


Une application utilisant ADO est composée des éléments suivants :
• Une interface utilisateur contenant des contrôles orientés données. Les
contrôles de données visuels sont facultatifs si l’accès aux données se fait par
code.
• Un ou plusieurs composants ensemble de données qui représentent les
informations provenant de tables ou de requêtes.
• Un composant source de données pour chaque composant ensemble de
données servant d’intermédiaire entre le composant ensemble de données et
les contrôles orientés données.

13-12 Guide du développeur


Applications basées sur ADO

• Un composant connexion pour se connecter au stockage de données ADO. Le


composant connexion sert d’intermédiaire entre les composants ensemble de
données de l’application et la base de données à laquelle on accède via le
stockage de données.
La couche ADO d’une application Delphi utilisant ADO est composée de
Microsoft ADO 2.1, d’un fournisseur OLE DB ou d’un pilote ODBC pour l’accès
au stockage de données, d’un programme client pour le système de base de
données utilisé (pour les bases de données SQL), d’un système de base de
données accessible depuis l’application (pour les systèmes de base de données
SQL) et d’une base de données. Toutes ces entités externes doivent être présentes
et accessibles pour qu’une application ADO puisse fonctionner.

Bases de données et ensembles de données ADO


La page ADO de la palette des composants contient tous les composants
nécessaires pour se connecter à des bases de données et utiliser leurs tables.
Tous les objets de métadonnée d’une application utilisant ADO sont contenus
dans la base de données à laquelle on accède via le stockage de données ADO.
Pour accéder à ces objets et à leurs données, une application doit tout d’abord se
connecter au stockage de données. Pour plus d’informations sur la manière de se
connecter à des stockages de données, voir “Connexion à des stockages de
données ADO” à la page 23-3.
Les données d’une base de données sont stockées dans une ou plusieurs tables.
Vous devez inclure dans votre application au moins l’un des composants
ensemble de données ADO (TADODataSet, TADOQuery, etc) pour pouvoir
manipuler les données stockées dans une base de données. Pour plus
d’informations sur l’utilisation de composants ensemble de données ADO pour
accéder aux données des tables, voir “Accès aux données” à la page 13-14 et
“Utilisation des ensembles de données ADO” à la page 23-12.

Connexion aux bases de données ADO


Une application ADO Delphi utilise ADO 2.1 pour interagir avec un fournisseur
OLE DB afin de se connecter à un stockage de données et accéder à ses données.
Un stockage de données peut, entre autre, représenter une base de données. Une
application utilisant ADO nécessite l’installation de ADO 2.1 sur le système
client. ADO et OLE DB sont fournis par Microsoft et installés avec Windows.
Le fournisseur peut représenter un ou plusieurs types d’accès, que ce soit des pilotes
natifs OLE DB ou des pilotes ODBC. Ces pilotes doivent également être installés
sur le système client. Des pilotes OLE DB sont proposés pour divers systèmes de
bases de données tant par le fournisseur de la base de données que par des tiers.
Si l’application utilise une base de données SQL comme Microsoft SQL Server ou
Oracle, le logiciel client de ce système de base de données doit également être
installé sur le système client. Les logiciels client sont fournis par le vendeur de la
base de données et sont installés depuis le CD (ou les disquettes) du système de
base de données.

Construction d’applications à niveau unique et à niveau double 13-13


Applications basées sur ADO

Pour connecter l’application au stockage de données, le composant connexion


ADO doit être configuré pour utiliser l’un des fournisseurs disponibles. Une fois
le composant connexion connecté au stockage de données, il est possible
d’associer les composants ensemble de données au composant connexion afin
d’accéder aux tables de la base de données. Pour des informations sur la
connexion aux stockages de données, voir “Connexion à des stockages de
données ADO” à la page 23-3.
En plus de l’accès à la base de données, le composant connexion propose à
l’application la gestion de transactions ADO.

Accès aux données


Quand une application dispose d’une connexion fonctionnelle avec une base de
données, il est possible d’utiliser les composants ensemble de données pour
accéder aux tables de la base de données. La page ADO de la palette des
composants contient les composants ensemble de données ADO nécessaires pour
accéder aux données des stockages de données ADO.
Ces composants sont TADODataSet, TADOTable, TADOQuery et TADOStoredProc.
Tous ces composants sont capables de lire des données dans des stockages de
données ADO, de modifier par code les données ou de proposer les données à
l’utilisateur de l’application pour une utilisation interactive. Pour davantage
d’informations sur l’utilisation des composants ensemble de données ADO afin
de lire et de modifier les données, voir “Utilisation des ensembles de données
ADO” à la page 23-12.
Pour que les données d’un ensemble de données ADO soient visuellement
accessibles dans une application, utilisez des contrôles orientés données. Il n’y a
pas de contrôles orientés données spécifiques pour ADO. Pour plus
d’informations sur l’utilisation des contrôles orientés données, voir
“Fonctionnalités communes des contrôles de données” à la page 26-1.
Le composant source de données standard est utilisé comme intermédiaire entre
les composants ensemble de données ADO et les contrôles orientés données.
ADO ne nécessite pas un composant source de données spécifique. Pour plus
d’informations sur l’utilisation des composants source de données, voir
“Utilisation des sources de données” à la page 26-6.
Si nécessaire, vous pouvez utiliser des objets champ persistant pour représenter
les champs des composants ensemble de données ADO. Comme pour les
contrôles orientés données et les composants source de données, utilisez
directement les classes de champ Delphi (TField et ses descendants). Pour plus
d’informations sur les objets champs dynamiques ou persistants, voir
“Présentation des composants champ” à la page 19-2.

13-14 Guide du développeur


Applications de base de données linéaires

Création et restructuration de tables de bases de données ADO


La création ou la suppression de métadonnées d’une base de données ADO dans
une application Delphi doit être faite en utilisant le SQL. De même la restructuration
de tables se fait à l’aide d’instructions SQL. La modification des autres objets de
métadonnée ne peut se faire directement. Vous devez d’abord, supprimer l’objet de
métadonnée puis le remplacer par un nouveau avec des attributs différents.
Il est possible d’accéder par le biais d’ADO à de nombreux types de bases de
données et les pilotes de ces types de bases de données spécifiques n’utilisent
pas tous la même syntaxe SQL. La présentation de toutes les syntaxes SQL
gérées par les différents type de bases de données dépasse le cadre de cette
documentation tout comme la description des différences entre ces types de
bases de données. Pour une présentation complète et à jour de l’implémentation
SQL d’un système de base de données précis, reportez-vous à sa documentation.
En général, utilisez l’instruction CREATE TABLE pour créer des tables dans la
base de données et CREATE INDEX pour créer de nouveaux index pour ces
tables. Quand cela est géré, utilisez d’autres instructions CREATE pour ajouter
d’autres objets de métadonnées (par exemple, CREATE DOMAIN, CREATE
VIEW ou CREATE SCHEMA).
Pour chaque instruction CREATE, il existe une instruction DROP correspondante
permettant de supprimer un objet de métadonnées. Parmi ces instructions, il y
a DROP TABLE, DROP VIEW, DROP DOMAIN et DROP SCHEMA.
Pour modifier la structure d’une table, utilisez l’instruction ALTER TABLE.
ALTER TABLE utilise les clauses ADD et DROP pour créer de nouveaux
éléments dans une table ou pour en supprimer. Utilisez, par exemple, la clause
ADD COLUMN pour ajouter une nouvelle colonne à la table et DROP
CONSTRAINT pour supprimer une contrainte de la table.
Exécutez ces commandes de métadonnée à partir de composants commande ou
requête ADO. Pour davantage d’informations sur l’utilisation des composants
commande ADO pour exécuter des commandes, voir “Exécution de commandes”
à la page 23-28.

Applications de base de données linéaires


Les applications de base de données linéaires sont des applications à niveau
unique qui utilisent TClientDataSet pour représenter tous leurs ensembles de
données. L’ensemble de données client conserve toutes ses données en mémoire,
ce qui signifie que ce type d’application n’est pas approprié pour les ensembles
de données très volumineux.
Les applications de base de données linéaires ne requièrent pas le moteur de
bases de données Borland (BDE) ni les objets de données ActiveX (ADO). A la
place, elles utilisent MIDAS.DLL. Lorsque seul MIDAS.DLL est utilisé, les
applications linéaires sont plus faciles à déployer car vous n’avez pas besoin
d’installer, de configurer ni de maintenir le logiciel de gestion des connexions
aux bases de données.

Construction d’applications à niveau unique et à niveau double 13-15


Applications de base de données linéaires

Comme ces applications n’utilisent pas de base de données, il n’y a pas de


support multi-utilisateur. Les ensembles de données sont entièrement dédiés à
l’application. Les données peuvent être enregistrées dans des fichiers linéaires
sur disque et chargées ultérieurement, mais aucune protection intégrée ne peut
éviter qu’un utilisateur n’écrase les fichiers de données d’un autre utilisateur.
Les ensembles de données client (qui se trouvent sur la page MIDAS de la
palette des composants) forment la base des applications de base de données
linéaires. Ils permettent de gérer la plupart des opérations de base de données
réalisées avec les autres ensembles de données. Vous utilisez les mêmes contrôles
orientés données et les mêmes composants source de données que dans une
application à niveau unique basée sur le BDE. Vous n’utilisez pas de composants
base de données car il n’y a aucune connexion de base de données ni aucune
transaction à gérer. Vous n’avez pas besoin de vous inquiéter des composants
session sauf si votre application est multithread. Pour plus d’informations sur
l’utilisation des ensembles de données client, voir chapitre 24, “Création et
utilisation d’un ensemble de données client”.
Les principales différences entre l’écriture d’applications de base de données
linéaire et celle d’autres applications à niveau unique concernent la création des
ensembles de données, le chargement et l’enregistrement des données.

Création des ensembles de données


Comme les applications de base de données linéaire n’utilisent pas les bases de
données existantes, vous devez créer les ensembles de données vous-même. Une
fois que l’ensemble de données est créé, vous pouvez l’enregistrer dans un
fichier. Vous n’aurez plus besoin de recréer la table ; vous devrez seulement la
charger à partir du fichier enregistré. Toutefois, les index ne sont pas enregistrés
avec la table. Vous devez les recréer chaque fois que vous chargez la table.
Lorsque vous démarrez une application de base de données, vous pouvez
commencer par créer et enregistrer des fichiers vides pour vos ensembles de
données puis passer à l’écriture même de l’application. Ainsi, vous n’avez pas
besoin de définir les métadonnées de vos ensembles de données client dans
l’application finale.
La façon dont vous créez votre ensemble de données client diffère selon que
vous créez entièrement un ensemble de données ou que vous convertissez une
application basée sur le BDE existant.

Création d’un nouvel ensemble de données à l’aide de champs persistants


Les étapes suivantes décrivent la création d’un nouvel ensemble de données
client à l’aide de l’éditeur de champs :
1 A partir de la page MIDAS de la palette des composants, ajoutez un
composant TClientDataSet à votre application.

13-16 Guide du développeur


Applications de base de données linéaires

2 Cliquez avec le bouton droit sur l’ensemble de données client et sélectionnez


Editeur de champs. Dans l’éditeur de champs, cliquez avec le bouton droit et
choisissez la commande Nouveau champ. Décrivez les principales propriétés
de votre définition de champ. Une fois que le champ est créé, vous pouvez
modifier ses propriétés dans l’inspecteur d’objets en le sélectionnant dans
l’éditeur de champs.
Ajoutez des champs dans l’éditeur de champs jusqu’à ce que vous ayez décrit
votre ensemble de données client.
3 Cliquez avec le bouton droit sur l’ensemble de données client et choisissez
Créer un DataSet. Cela permet de créer un ensemble de données client vide à
partir des champs persistants ajoutés dans l’éditeur de champs.
4 Cliquez avec le bouton droit sur l’ensemble de données client et choisissez
Enregistrer sous. Cette commande n’est disponible que si l’ensemble de
données client contient des données.
5 Dans la boîte de dialogue Enregistrer, choisissez un nom de fichier et
enregistrez une copie du fichier linéaire de votre ensemble de données client.
Remarque Vous pouvez aussi créer l’ensemble de données client en mode exécution à l’aide
de champs persistants qui sont enregistrés avec l’ensemble de données client. Il
suffit d’appeler la méthode CreateDataSet.

Création d’un ensemble de données à l’aide de définitions de champ et


d’index
La création d’un ensemble de données à l’aide de définitions de champ et
d’index s’apparente à la création d’une table de base de données à l’aide d’un
composant TTable. Il n’y a pas de propriété DatabaseName, TableName ou
TableType à définir puisqu’elles ne sont pas pertinentes pour les ensembles de
données client. Toutefois, comme dans le cas du composant TTable, vous utilisez
la propriété FieldDefs pour spécifier les champs de votre table et la propriété
IndexDefs pour spécifier tous les index. Une fois que la table est spécifiée, cliquez
avec le bouton droit sur l’ensemble de données et choisissez Créer un DataSet en
mode conception ou appelez la méthode CreateDataSet en mode exécution.
Lorsque vous définissez les index de votre ensemble de données client, deux
propriétés s’appliquent uniquement à ce dernier : TIndexDef.DescFields et
TIndexDef.CaseInsFields.
DescFields vous permet de définir des index qui trient les enregistrements en
ordre croissant sur certains champs et décroissant sur d’autres champs. Au lieu
d’utiliser l’option ixDescending pour trier en ordre décroissant sur tous les
champs de l’index, spécifiez uniquement les champs à trier en ordre décroissant
sous la valeur DescFields. Par exemple, lorsque vous définissez un index avec tri
sur Field1, puis Field2, puis Field3, et que vous attribuez au paramètre DescFields
la valeur
Field1;Field3
vous obtenez un index avec tri croissant sur Field2 et décroissant sur Field1 et
Field3.

Construction d’applications à niveau unique et à niveau double 13-17


Applications de base de données linéaires

CaseInsFields vous permet de définir des index qui trient les enregistrements en
tenant compte de la casse sur certains champs et sans en tenir compte sur
d’autres. Au lieu d’utiliser l’option isCaseInsensitive pour trier tous les champs de
l’index en tenant compte de la casse, spécifiez uniquement les champs à trier
sans tenir compte de la casse sous la valeur CaseInsFields. Comme DescFields,
CaseInsFields accepte une liste de noms de champ séparés par des points-virgules.
Vous pouvez spécifier les définitions de champ et d’index en mode conception à
l’aide de l’éditeur de collection. Il suffit de choisir la propriété appropriée dans
l’inspecteur d’objets (FieldDefs ou IndexDefs) et de double-cliquer pour afficher
l’éditeur de collection. Utilisez l’éditeur de collection pour ajouter, supprimer et
réorganiser les définitions. En sélectionnant les définitions dans l’éditeur de
collection, vous pouvez modifier leurs propriétés dans l’inspecteur d’objets.
Vous pouvez aussi spécifier les définitions de champ et d’index dans du code en
mode exécution. Par exemple, le code suivant crée et active un ensemble de
données client dans le gestionnaire d’événements OnCreate de la fiche :
procedure TForm1.FormCreate(Sender: TObject);
begin
with ClientDataSet1 do
begin
with FieldDefs.AddFieldDef do
begin
DataType := ftInteger;
Name := 'Field1';
end;
with FieldDefs.AddFieldDef do
begin
DataType := ftString;
Size := 10;
Name := 'Field2';
end;
with IndexDefs.AddIndexDef do
begin
Fields := 'Field1';
Name := 'IntIndex';
end;
CreateDataSet;
end;
end;

Création d’un ensemble de données à partir d’une table existante


Si vous convertissez une application existante basée sur le BDE en une
application à niveau unique linéaire, vous pouvez copier des tables existantes et
les enregistrer en tant que tables linéaires à partir de l’EDI. Les étapes suivantes
montrent comment copier une table existante :
1 A partir de la page AccèsBD de la palette des composants, ajoutez un
composant TTable à votre application. Définissez ses propriétés DatabaseName
et TableName afin d’identifier la table base de données existante. Attribuez à sa
propriété Active la valeur True.

13-18 Guide du développeur


Applications de base de données linéaires

2 A partir de la page MIDAS de la palette des composants, ajoutez un


composant TClientDataSet.
3 Cliquez avec le bouton droit sur l’ensemble de données client et sélectionnez
Affecter données locales. Dans la boîte de dialogue qui apparaît, choisissez le
composant table ajouté à l’étape 1. Choisissez OK.
4 Cliquez avec le bouton droit sur l’ensemble de données client et choisissez
Enregistrer sous. Cette commande n’est disponible que si l’ensemble de
données client contient des données.
5 Dans la boîte de dialogue Enregistrer, choisissez un nom de fichier et
enregistrez une copie du fichier linéaire de votre table de base de données.

Chargement et enregistrement des données


Dans les applications de base de données linéaires, toutes les modifications de la
table sont uniquement consignées dans un journal des modifications en mémoire.
Ce journal est géré séparément des données elles-mêmes bien qu’il soit
complètement transparent pour les objets qui utilisent l’ensemble de données
client. En d’autres termes, les contrôles qui naviguent dans l’ensemble de
données client ou affichent ses données ont un aperçu des données qui intègre
les modifications. Si vous ne souhaitez pas annuler les modifications, vous devez
fusionner le journal des modifications avec les données de l’ensemble de données
client en appelant la méthode MergeChangeLog. Pour plus d’informations sur le
journal des modifications, voir “Edition des données” à la page 24-5.
Lorsque vous avez fusionné les modifications avec les données de l’ensemble de
données client, ces dernières n’existent qu’en mémoire. Bien qu’elles ne soient
pas perdues si vous fermez puis ouvrez à nouveau l’ensemble de données client
dans votre application, l’arrêt de celle-ci les fera disparaître. Pour que ces
données soient permanentes, elles doivent être écrites sur disque. Enregistrez les
modifications sur disque à l’aide de la méthode SaveToFile. SaveToFile accepte un
paramètre, le nom du fichier qui est créé (ou écrasé) et qui contient la table.
Lorsque vous souhaitez lire une table précédemment écrite à l’aide de la
méthode SaveToFile, utilisez la méthode LoadFromFile. LoadFromFile accepte aussi
un paramètre, le nom du fichier contenant la table.
Lorsque vous enregistrez un ensemble de données client, les métadonnées qui
décrivent la structure des enregistrements sont enregistrées avec l’ensemble de
données, mais les index ne le sont pas. Vous pouvez donc ajouter du code qui
recrée les index lorsque vous chargez les données à partir du fichier. Vous
pouvez aussi écrire votre application de sorte qu’elle crée automatiquement les
index à la volée.
Si vous chargez les données toujours à partir du même fichier et les y
enregistrez toujours, vous pouvez utiliser la propriété FileName au lieu des
méthodes SaveToFile et LoadFromFile. Lorsque FileName a pour valeur un nom de
fichier valide, les données sont automatiquement chargées à partir du fichier à
l’ouverture de l’ensemble de données client et enregistrées dans le fichier à la
fermeture de l’ensemble de données client.

Construction d’applications à niveau unique et à niveau double 13-19


Applications de base de données linéaires

Utilisation du modèle “briefcase”


Dans la plus grande partie de cette section, nous avons décrit la création et
l’utilisation d’un ensemble de données client dans une application à niveau
unique. Le modèle à niveau unique peut être combiné avec un modèle
multiniveau pour créer un modèle appelé "briefcase".
Remarque Le modèle “briefcase” est parfois appelé modèle “déconnecté” ou informatique
nomade.
Lors d’une opération sur site, une application “briefcase” ressemble à une
application multiniveau : un utilisateur démarre l’application client sur une
machine et se connecte par le réseau à un serveur d’applications situé sur une
machine distante. Le client demande des données au serveur d’applications et lui
renvoie des mises à jour. Les mises à jour sont appliquées par le serveur
d’applications à une base de données qui peut être partagée avec d’autres clients
dans une entreprise.
Supposons, toutefois, que la base de données sur site de votre entreprise
contienne des informations de contacts clients que vos représentants peuvent
utiliser et mettre à jour à l’extérieur de l’entreprise. Dans ce cas, il serait
intéressant que vos représentants puissent télécharger tout ou partie des données
de la base de données de l’entreprise, travailler sur ces données pendant qu’ils
sont en déplacement dans tout le pays, et même mettre à jour des
enregistrements sur des sites existants ou de nouveaux sites clients. Quand les
représentants reviennent dans l’entreprise, ils doivent charger leurs changements
de données dans la base de données de l’entreprise pour les mettre à la
disposition de tous. Cette possibilité de manipuler des données en mode
autonome puis d’appliquer ensuite des mises à jour sur les données en ligne est
connue sous le nom de modèle “briefcase”.
En utilisant le modèle "briefcase", vous pouvez profiter de la capacité du
composant ensemble de données client à lire et à écrire des données dans des
fichiers linéaires pour créer des applications client susceptibles d’être utilisées à
la fois en ligne avec un serveur d’applications et en mode autonome en tant
qu’applications temporaires à liaison unique.
Pour implémenter le modèle “briefcase”, vous devez réaliser les tâches
suivantes :
1 Créez une application serveur multiniveau comme décrit dans “Création du
serveur d’applications” à la page 14-13.
2 Créez une application de base de données linéaire comme application client.
Ajoutez un composant connexion et définissez la propriété RemoteServer de vos
ensembles de données client pour spécifier ce composant connexion. Cela leur
permet de communiquer avec le serveur d’applications créé à l’étape 1. Pour
plus d’informations sur les composants connexion , voir “Connexion au
serveur d’applications” à la page 14-22.
3 Dans l’application client, essayez au démarrage de vous connecter à
l’application serveur. Si la connexion échoue, invitez l’utilisateur à choisir un
fichier et lire la copie locale des données.

13-20 Guide du développeur


Passage à une application à niveau triple

4 Dans l’application client, ajoutez du code pour appliquer les mises à jour dans
le serveur d’applications. Pour plus d’informations sur l’envoi de mises à jour
à partir d’une application client vers un serveur d’applications, voir “Mise à
jour des enregistrements” à la page 24-24.

Passage à une application à niveau triple


Dans une application client/serveur à niveau double, l’application est un client
qui communique directement avec le serveur de bases de données. Même dans ce
schéma, l’application peut sembler être composée de deux parties : une connexion
de base de données et une interface utilisateur. Pour transformer une application
client/serveur à niveau double en une application multiniveau, vous devez :
• Scinder votre application existante afin d’obtenir un serveur d’applications, qui
gère la connexion de base de données, et une application client, qui contient
l’interface utilisateur.
• Ajouter une interface entre le client et le serveur d’applications.
Plusieurs procédures sont possibles. Mais les étapes suivantes constituent une
procédure minimale pour le bon fonctionnement de votre conversion :
1 Créez un nouveau projet pour le serveur d’applications, en commençant avec
un module de données distant. Pour plus de détails, voir “Création du
serveur d’applications” à la page 14-13.
2 Dupliquez les portions pertinentes de connexion de base de données de votre
ancienne application à niveau double et, pour chaque ensemble de données,
ajoutez un composant fournisseur qui servira de liaison de données entre le
serveur d’applications et le client. Pour plus d’informations sur l’utilisation
d’un composant fournisseur, voir chapitre 15, “Utilisation des composants
fournisseur”.
3 Copiez votre projet à niveau double existant, supprimez ses connexions de
base de données directes et ajoutez-y un composant connexion approprié.
Pour plus d’informations sur la création et l’utilisation des composants
connexion, voir “Connexion au serveur d’applications” à la page 14-22.
4 Substituez un ensemble de données client à chaque composant ensemble de
données activé par le BDE ou par les ADO dans le projet original. Pour plus
d’informations sur l’utilisation d’un composant ensemble de données client, voir
chapitre 24, “Création et utilisation d’un ensemble de données client”
5 Dans l’application client, ajoutez du code pour appliquer les mises à jour dans
le serveur d’applications. Pour plus d’informations sur l’envoi des mises à jour
à partir d’une application client vers un serveur d’applications, voir “Mise à
jour des enregistrements” à la page 24-24.
6 Déplacez les composants ensemble de données vers les modules de données
du serveur d’applications. Définissez la propriété DataSet de chaque
fournisseur pour spécifier les ensembles de données correspondants. Pour plus
d’informations sur la liaison d’un ensemble de données à un composant
fournisseur, voir chapitre 15, “Utilisation des composants fournisseur”.

Construction d’applications à niveau unique et à niveau double 13-21


13-22 Guide du développeur
Chapitre

Création d’applications multiniveaux


Chapter 14
14
Ce chapitre décrit la création d’une application de base de données client/
serveur multiniveau. Une application client/serveur multiniveau est partitionnée
en unités logiques dont les différentes unités fonctionnent en coordination les
unes avec les autres sur des machines séparées. Les applications multiniveaux
partagent des données et communiquent par un réseau local voire même par
Internet. Elles offrent de nombreux avantages, comme les applications clients
simples et la logique d’entreprise centralisée.
Dans sa forme la plus simple, parfois appelée « modèle à niveau triple », une
application multiniveau est partitionnée en trois niveaux :
• une application client : elle fournit une interface utilisateur sur la machine de
l’utilisateur ;
• un serveur d’applications : il réside dans un emplacement central du réseau,
accessible à tous les clients, et offre des services de données courants ;
• un serveur de bases de données distant : il supporte le système de gestion de
base de données relationnelles (SGBDR).
Dans ce modèle à niveau triple, le serveur d’applications gère les flux de
données entre les clients et le serveur de base de données distant ; il est donc
parfois dénommé « courtier ou broker de données ». Avec Delphi, on ne crée
généralement que le serveur d’applications et ses clients, bien que, si vous êtes
réellement ambitieux, vous puissiez créer également votre propre dorsal de base
de données.
Dans les applications multiniveaux plus complexes, les services supplémentaires
se situent entre un client et un serveur de base de données distant. Vous pouvez,
par exemple, disposer d’un courtier de services de sécurité pour prendre en
charge la sécurité des transactions Internet ou de services de pont pour prendre
en charge le partage de données avec des bases de données résidant sur des
plates-formes qui ne sont pas directement supportées par Delphi.

Création d’applications multiniveaux 14-1


Avantages du modèle de base de données multiniveau

Le support Delphi des applications multiniveaux repose sur MIDAS (Multi-tier


Distributed Application Services Suite). Ce chapitre se focalise sur la création
d’une application de bases de données à niveau triple à l’aide de la technologie
MIDAS. Quand vous savez créer et gérer une application à niveau triple, vous
pouvez créer et ajouter des couches de services supplémentaires en fonction de
vos besoins.

Avantages du modèle de base de données multiniveau


Le modèle de base de données multiniveau divise une application de base de
données en unités logiques. L’application client peut se focaliser sur l’affichage
des données et sur les interactions utilisateur. Dans sa forme idéale, elle ignore la
façon dont les données sont stockées ou gérées. Le serveur d’applications (niveau
intermédiaire) coordonne et traite les requêtes et les mises à jour de différents
clients. Il gère tous les détails de la définition des ensembles de données et de
l’interaction avec le serveur de bases de données distant.
Les avantages de ce modèle multiniveau sont les suivants :
• Encapsulation de la logique de l’entreprise dans un niveau intermédiaire
partagé. Toutes les applications client accèdent au même niveau intermédiaire.
Cela évite la redondance et le coût de gestion liés à la duplication des règles
de l’entreprise pour chaque application.
• Applications client simples. Vous pouvez écrire vos applications client de
sorte qu’elles occupent peu de place et déléguer ainsi une part plus
importante du traitement aux niveaux intermédiaires. Non seulement les
applications client sont de taille réduite, mais elles sont plus faciles à déployer
car elles ne sont pas concernées par l’installation, la configuration et la gestion
du logiciel de connectivité des bases de données (comme le moteur de bases
de données Borland). Les applications client simples peuvent être distribuées
sur internet pour bénéficier de plus de souplesse.
• Traitement distribué des données. La répartition du travail d’une application
entre plusieurs machines peut améliorer les performances par un meilleur
équilibrage de la charge et permet aux systèmes redondants de prendre le
relais en cas de défaillance d’un serveur.
• Possibilité d’améliorer la sécurité. Vous pouvez isoler les fonctionnalités
sensibles dans des niveaux sur lesquels sont appliquées différentes restrictions
d’accès. Vous bénéficiez ainsi de niveaux de sécurité souples et configurables.
Les niveaux intermédiaires peuvent limiter les points d’entrée des supports
sensibles, ce qui vous permet de mieux en contrôler l’accès. Si vous utilisez
HTTP, CORBA, ou MTS, vous pouvez tirer parti de leur modèle de sécurité.

14-2 Guide du développeur


Présentation de la technologie MIDAS

Présentation de la technologie MIDAS


MIDAS offre un mécanisme qui permet aux applications client et aux serveurs
d’applications d’échanger des informations de base de données. L’utilisation de
MIDAS requiert MIDAS.DLL, dont se servent les applications client et serveur
pour gérer les ensembles de données stockées sous forme de paquets de
données. La construction des applications MIDAS peut également nécessiter
l’explorateur SQL qui facilite la gestion des bases de données et qui permet
d’importer les contraintes serveur dans le dictionnaire des données, de sorte
qu’elles peuvent être contrôlées à n’importe quel niveau d’une application
multiniveau.
Remarque Vous devez acquérir des licences serveur pour déployer vos applications MIDAS.
Les applications multiniveaux basées sur MIDAS utilisent les composants de la
page MIDAS de la palette des composants ainsi qu’un module de données
distant créé par un expert de la page Multi-niveaux de la boîte de dialogue
Nouveaux éléments. Ces composants sont décrit dans le tableau 14.1:

Tableau 14.1 Composants MIDAS


Composant Description
modules de Modules de données spécialisés agissant commeun serveur COM
données distants Automation ou serveur CORBA pour permettre aux applications client
d’accéder à tous les fournisseurs qu’ils contiennent. Utilisé sur
l’application serveur.
composant Courtier de données qui offre des données en créant des paquets de
fournisseur données et qui résout les mise à jours client. Utilisé sur l’application
serveur.
composant Ensemble de données spécialisé qui utilise MIDAS.DLL pour gérer les
ensemble de données stockées sous forme de paquets de données.
données client
composants Famille de composants qui localisent le serveur, établissent les
connexion connexions et mettent l’interface IAppServer à la disposition des
ensembles de données client. Chaque composant connexion utilise un
protocole de communication particulier.

Présentation d’une application multiniveau basée sur MIDAS


Les étapes numérotées suivantes illustrent une suite normale d’événements pour
une application multiniveau basée sur MIDAS :
1 Un utilisateur démarre l’application client. Le client se connecte au serveur
d’applications (qui peut être spécifié lors de la conception ou lors de
l’exécution). Si le serveur d’applications n’est pas déjà en fonctionnement, il
démarre. Le client reçoit une interface IAppServer du serveur d’applications.
2 Le client demande des données au serveur d’applications. Un client peut
demander toutes les données à la fois ou bien demander les données par
fragments durant la session (extraction sur demande).

Création d’applications multiniveaux 14-3


Présentation de la technologie MIDAS

3 Le serveur d’applications récupère les données (si nécessaire, en établissant


d’abord une connexion avec la base de données), les empaquette pour le client
puis renvoie un paquet de données au client. D’autres informations (par
exemple, relatives aux contraintes de données imposées par la base de
données) peuvent être incluses dans les métadonnées du paquet de données.
Ce processus d’empaquetage des données est appelé “fourniture”.
4 Le client décode le paquet de données et affiche les données à l’utilisateur.
5 Pendant que l’utilisateur interagit avec l’application client, les données sont
mises à jour (des enregistrements sont ajoutés, supprimés ou modifiés). Ces
modifications sont stockées par le client dans un journal de modifications.
6 Le client applique ensuite les mises à jour au serveur d’applications,
généralement en réponse à une action de l’utilisateur. Pour appliquer les mises
à jour, le client empaquette son journal des modifications et le transmet en
tant que paquet de données au serveur.
7 Le serveur d’applications décode le paquet et émet les mises à jour dans le
contexte d’une transaction. Si un enregistrement ne peut pas être validé dans
le serveur (par exemple parce qu’une autre application a modifié
l’enregistrement après que le client l’a demandé et avant que le client n’ait
appliqué ses mises à jour), le serveur d’applications essaie de régulariser les
modifications du client avec les données courantes ou enregistre les
enregistrements qui n’ont pu être validés. Cette opération de validation des
enregistrements et de sauvegarde des enregistrements à problème est
dénommée “résolution”.
8 Après l’opération de résolution, le serveur d’applications renvoie au client les
enregistrements non transmis pour résolution ultérieure.
9 Le client régularise les enregistrements non résolus. Cela peut s’effectuer de
plusieurs manières. Le client tente habituellement de remédier à la situation
qui a empêché la validation des enregistrements ou bien annule les
changements. Si l’erreur peut être corrigée, le client applique de nouveau les
mises à jour.
10 Le client rafraîchit ses données à partir du serveur.

Structure de l’application client


Pour l’utilisateur final, l’application client d’une application multiniveau a
l’apparence et le comportement d’une application à niveau double qui utilise les
mises à jour en cache. Du point de vue de la structure, l’application client
ressemble beaucoup à une application à niveau unique linéaire. L’interaction
utilisateur s’opère par le biais de contrôles orientés données standard qui
affichent les données à partir d’un composant ensemble de données client. Pour
plus de détails sur l’utilisation des propriétés, événements et méthodes des
ensembles de données client, voir chapitre 24, “Création et utilisation d’un
ensemble de données client”

14-4 Guide du développeur


Présentation de la technologie MIDAS

A la différence de ce qui se passe dans une application linéaire, dans une


application multiniveau, l’ensemble de données client obtient ses données par le
biais de l’interface IAppServer sur le serveur d’applications. Il utilise aussi cette
interface pour valider les mises à jour sur le serveur d’applications. Pour plus
d’informations sur l’interface IAppServer, voir “Utilisation de l’interface
IAppServer” à la page 14-8. Le client obtient cette interface auprès d’un
composant connexion.
Le composant connexion établit la connexion au serveur d’applications.
Différents composants connexion sont disponibles suivant les protocoles de
communication utilisés. Ces composants connexion sont présentés dans le tableau
suivant :

Tableau 14.2 Composants connexion


composant protocole
TDCOMConnection DCOM
TSocketConnection Windows sockets (TCP/IP)
TWebConnection HTTP
TOLEnterpriseConnection OLEnterprise (RPC)
TCorbaConnection CORBA (IIOP)

Remarque Deux autres composants connexion, TRemoteServer et TMIDASConnection, sont


disponibles pour garantir une compatibilité ascendante.
Pour plus d’informations sur l’utilisation des composants connexion, voir
“Connexion au serveur d’applications” à la page 14-22.

Structure du serveur d’applications


Le serveur d’applications comprend un module de données distant qui offre une
interface IAppServer utilisée par les applications client pour communiquer avec
les fournisseurs de données. Il existe trois types de modules de données
distants :
• TRemoteDataModule : il s’agit d’un serveur Automation à double interface.
Utilisez ce type de module de données distant si les clients utilisent DCOM,
HTTP, Sockets ou OLEnterprise pour se connecter au serveur d’applications,
sauf si vous souhaitez installer le serveur d’applications avec MTS.
• TMTSDataModule : il s’agit d’un serveur Automation à double interface.
Utilisez ce type de module de données distant si vous créez le serveur
d’applications en tant que bibliothèque active (.DLL) installée avec MTS. Vous
pouvez utiliser des modules de données distants MTS avec DCOM, HTTP,
Sockets ou OLEnterprise.
• TCorbaDataModule : il s’agit d’un serveur CORBA. Utilisez ce type de
module de données distant pour fournir des données aux clients CORBA.

Création d’applications multiniveaux 14-5


Présentation de la technologie MIDAS

Vous pouvez inclure un composant non visuel dans le module de données


distant comme dans n’importe quel module de données. En outre, le module de
données distant contient un composant fournisseur pour chaque ensemble de
données disponible pour les applications client. Un fournisseur d’ensemble de
données permet de :
• Recevoir les requêtes de données du client, extraire les données requises du
serveur de bases de données, empaqueter les données pour les transmettre et
envoyer les données à l’ensemble de données client. Cette activité est appelée
“fourniture”.
• Recevoir les données mises à jour de l’ensemble de données client, appliquer
les mises à jour dans la base de données ou la source de l’ensemble de
données et consigner toutes les mises à jour inapplicables (en renvoyant les
mises à jour non résolues au client pour régularisation ultérieure). Cette
activité est appelée “résolution”.
Souvent, le fournisseur utilise des ensembles de données BDE ou ADO tels qu’on
en trouve dans les applications à niveau double. En fonction de vos besoin, vous
pouvez ajouter des composants bases de données et sessions, comme dans une
application à niveau double basée sur le BDE, ou des composants connexion
ADO, comme dans une application à niveau double basée sur le ADO.
Remarque Ne confondez pas le composant connexion ADO, qui ressemble à un composant
base de données dans une application basée sur le BDE, avec les composants
connexion utilisés par les applications client d’une application multiniveau.
Pour plus d’information sur les applications à niveau double, voir chapitre 13,
“Construction d’applications à niveau unique et à niveau double”.
Si le serveur d’applications gère MTS, le module de données MTS contient des
événements pour l’activation et la désactivation du serveur d’applications. Cela
permet au serveur d’applications d’acquérir des connexions de base de données
quand il est activé et de les libérer quand il est désactivé.

Utilisation de MTS
L’utilisation de MTS permet à votre module de données distant de tirer parti des
éléments suivants :
• Sécurité MTS. MTS offre à votre serveur d’applications une sécurité à base de
rôles. Des rôles sont attribués aux clients et déterminent comment ces derniers
peuvent accéder à l’interface du module de données MTS. Le module de
données MTS implémente la méthode IsCallerInRole, qui vous permet de
vérifier le rôle du client alors connecté et d’autoriser certaines fonctions selon
ce rôle. Pour plus d’informations sur la sécurité MTS, voir “Sécurité en
fonction des rôles” à la page 50-12.
• Regroupement des handles de base de données. Les modules de données
MTS regroupent automatiquement les connexions de base de données de sorte
que, lorsqu’un client n’utilise plus une connexion de base de données, un
autre client la réutilise. Ceci allège le trafic réseau car votre niveau

14-6 Guide du développeur


Présentation de la technologie MIDAS

intermédiaire n’a pas besoin de se déconnecter du serveur de bases de


données distant et de s’y reconnecter. Lorsque les handles de base de données
sont regroupés, votre composant base de données doit affecter à la propriété
KeepConnection la valeur False, pour que votre application optimise le partage
des connexions.
• Transactions MTS. Lorsque vous utilisez MTS, vous pouvez intégrer vos
propres transactions MTS dans le serveur d’applications pour offrir un
support amélioré des transactions. Les transactions MTS peuvent englober
plusieurs bases de données ou inclure des fonctions n’en impliquant aucune.
Pour plus d’informations sur les transactions, voir “Gestion des transactions
dans les applications multiniveaux” à la page 14-29.
• Activation “juste à temps” et désactivation “dès que possible”. Vous pouvez
écrire votre serveur MTS de sorte que les instances du module de données
distant soient activées et désactivées en fonction des besoins. Lorsque vous
utilisez l’activation “juste à temps” et la désactivation “dès que possible”,
votre module de données distant est instancié uniquement lorsqu’il est requis
pour traiter des requêtes client. Cela lui évite de mobiliser les handles de base
de données inutilisés.
L’utilisation de l’activation “juste à temps” et de la désactivation “dès que
possible” offre un juste milieux entre le routage de tous les clients par le biais
d’une seule instance du module de données distant et la création d’une
instance pour chaque connexion client. Avec une seule instance du module de
données distant, le serveur d’applications doit traiter tous les appels de base
de données par le biais d’une seule connexion de base de données. Cela
aboutit à un goulet d’étranglement et peut pénaliser les performances s’il y a
trop de clients. Avec plusieurs instances du module de données distant,
chaque instance peut gérer une connexion de base de données, ce qui évite de
sérialiser les accès aux bases de données. Toutefois, cela monopolise les
ressources car les autres clients ne peuvent pas utiliser la connexion à la base
de données tant qu’elle est associée au module de données distant d’un client.
Pour tirer parti des transactions, de l’activation “juste à temps” et de la
désactivation “dès que possible”, les instances du module de données distant
doivent être sans état. Cela signifie que vous devez fournir plus de support
lorsque votre client utilise des informations d’état. Par exemple, le client doit
passer les informations sur l’enregistrement en cours lors des lectures
incrémentales. Pour plus de renseignements sur les informations d’état et les
modules de données distants dans les applications multiniveaux, voir “Gestion
des informations d’état dans les modules de données distants” à la page 14-30.
Par défaut, tous les appels à un module de données MTS générés
automatiquement sont transactionnels (ils supposent qu’à la fin de l’appel le
module de données MTS peut être désactivé et que toutes les transactions en
cours éventuelles peuvent être validées (commit) ou annulées (rollback). Vous
pouvez écrire un module de données MTS dépendant d’informations d’état
persistantes en définissant par False la propriété AutoComplete, mais il ne
supportera pas les transactions, ni l’activation “juste à temps”, ni la désactivation
“dès que possible”.

Création d’applications multiniveaux 14-7


Présentation de la technologie MIDAS

Attention Lorsque vous utilisez MTS, les connexions aux bases de données ne doivent pas
être ouvertes avant l’activation du module de données distant. Pendant le
développement de votre application, assurez-vous que tous les ensembles de
données sont inactifs et que la base de données n’est pas connectée avant
l’exécution de votre application. Dans l’application elle-même, vous devez ajouter
du code pour ouvrir les connexions de base de données lors de l’activation du
module de données et pour les fermer lors de sa désactivation.

Regroupement des modules de données distants


Le regroupement des objets fait bénéficier de certains avantages accordés par
MTS lorsque vous n’utilisez pas DCOM. Avec le regroupement des objets, vous
pouvez limiter le nombre des instances créées pour votre module de données
distant. Cela réduit le nombre de connexions aux bases de données que vous
devez gérer et économise les autres ressources utilisées par le module de
données distant.
Lorsque le serveur reçoit les demandes des clients, il les passe au premier
module de données distant disponible du groupe. S’il n’y a pas de module
disponible, il en crée un nouveau (jusqu’au nombre maximal que vous avez
spécifié). C’est une solution intermédiaire entre le routage de tous les clients via
une seule instance du module de données distant (qui peut créer un goulet
d’étranglement) et la création d’une instance séparée pour chaque connexion
client (qui gaspille de nombreuses ressources).
Si une instance de module de données distant du groupe ne reçoit pas de
demande client pendant un moment, elle est automatiquement libérée. Cela
empêche le groupe de monopoliser les ressources inutilement.
Comme une même instance du module de données distant peut gérer les
demandes de plusieurs clients, elle ne doit pas reposer sur des informations
d’état persistantes. Voir “Gestion des informations d’état dans les modules de
données distants” à la page 14-30 pour savoir comment vous assurer que votre
module de données distant est sans état.
Pour tirer parti du regroupement des objets, votre module de données distant
doit surcharger la méthode UpdateRegistry. Dans la méthode surchargée, vous
pouvez appeler RegisterPooled lorsque le module de données distant est recensé
et UnregisterPooled lorsque le module de données distant est est dérecensé.
Vous pouvez tirer parti du regroupement des objets uniquement lorsque la
connexion est établie en utilisant HTTP.

Utilisation de l’interface IAppServer


Les modules de données distants sur le serveur d’applications prennent en
charge l’interfaceIAppServer. Les composants connexion des applications client
recherchent cette interface pour établir des connexions.
IAppServer instaure une passerelle entre les applications client et les composants
fournisseur du serveur d’applications. La plupart des applications client
n’utilisent pas l’interface IAppServer directement, mais l’appellent indirectement
par le biais des propriétés et des méthodes de l’ensemble de données client.

14-8 Guide du développeur


Présentation de la technologie MIDAS

Toutefois, si nécessaire, vous pouvez appeler directement l’interface IAppServer à


l’aide de la propriété AppServer de l’ensemble de données client.
Le tableau 14.3 présente la liste des méthodes de l’interface IAppServer, ainsi que des
méthodes et événements correspondants pour le composant fournisseur et
l’ensemble de données client. Ces méthodes IAppServer incluent un paramètre
Provider qui indique le fournisseur du serveur d’applications devant fournir les
données et résoudre les mises à jour. En outre, la plupart de ces méthodes acceptent
un paramètre OleVariant appelé OwnerData qui permet à l’application client et au
serveur d’applications de passer des informations personnalisées dans un sens et
dans l’autre. OwnerData n’est pas utilisé par défaut, mais il est transmis à tous les
gestionnaires d’événements pour l’écriture du code permettant à votre serveur
d’applications d’ajuster cette information avant et après chaque appel client.

Table 14.3 Membres de l’interface IAppServer


IAppServer composant fournisseur TClientDataSet
Méthode AS_ApplyUpdates Méthode ApplyUpdates, Méthode ApplyUpdates,
événement événement
BeforeApplyUpdates, BeforeApplyUpdates,
événement événement AfterApplyUpdates.
AfterApplyUpdates
Méthode AS_DataRequest Méthode DataRequest, Méthode DataRequest.
événement OnDataRequest
Méthode AS_Execute Méthode Execute, Méthode Execute,
événement BeforeExecute, événement BeforeExecute,
événement AfterExecute événement AfterExecute.
Méthode AS_GetParams Méthode GetParams, Méthode FetchParams,
événement événement BeforeGetparams,
BeforeGetParams, événement AfterGetParams.
événement AfterGetParams
Méthode Utilisée pour identifier tous Utilisée pour créer lors de la
AS_GetProviderNames les fournisseurs disponibles. conception une liste pour la
propriété ProviderName.
MéthodeAS_GetRecords Méthode GetRecords, Méthode GetNextPacket,
événement propriété Data,
BeforeGetRecords, événement BeforeGetRecords,
événement AfterGetRecords événement AfterGetRecords
Méthode AS_RowRequest Méthode RowRequest, Méthode FetchBlobs,
événement méthode FetchDetails,
BeforeRowRequest, méthode RefreshRecord,
événement événement BeforeRowRequest,
AfterRowRequest événement AfterRowRequest

Sélection d’un protocole de connexion


Chaque protocole de communication que vous pouvez utiliser pour connecter
vos applications client au serveur d’applications offre ses propres avantages.
Avant de choisir un protocole, déterminez le nombre de clients attendus, le
déploiement de votre application et les plans de développement futurs.

Création d’applications multiniveaux 14-9


Présentation de la technologie MIDAS

Utilisation de connexions DCOM


Le protocole DCOM offre l’approche de communication la plus directe car il ne
requiert aucune application d’exécution particulière sur le serveur. Toutefois,
comme DCOM n’est pas inclus dans Windows 95, il peut ne pas être installé sur
les machines client.
DCOM offre la seule approche qui vous permette d’utiliser la sécurité MTS. La
sécurité MTS repose sur l’attribution de rôles aux appelants d’objets MTS. En cas
d’appel vers MTS à l’aide de DCOM, DCOM indique à MTS l’application client
génératrice de l’appel. MTS peut alors déterminer précisément le rôle de
l’appelant. Avec d’autres protocoles, toutefois, un exécutable runtime, séparé du
serveur d’applications, reçoit les appels clients. Cet exécutable runtime réalise les
appels COM vers le serveur d’applications pour le compte du client. MTS ne
peut pas affecter de rôles à différents clients car, du point de vue de MTS, tous
les appels vers le serveur d’applications sont réalisés par l’exécutable runtime.
Pour plus d’informations sur la sécurité MTS, voir “Sécurité en fonction des
rôles” à la page 50-12.

Utilisation de connexions Socket


TCP/IP Sockets vous permet de créer des clients légers. Par exemple, si vous
écrivez une application client pour le Web, vous ne pouvez pas être certain que
les systèmes client supportent DCOM. Le protocole Sockets offre un plus petit
dénominateur commun qui permet d’établir des connexions au serveur
d’applications. Pour plus d’informations sur les sockets, voir chapitre 30,
“Utilisation des sockets”.
Au lieu d’instancier directement le module de données distant à partir du client
(comme c’est le cas avec DCOM), le protocole Sockets utilise une application
séparée sur le serveur (ScktSrvr.exe) qui accepte les requêtes client et instancie le
module de données distant à l’aide de COM. Le composant connexion sur le
client et ScktSrvr.exe sur le serveur sont responsables du tri des appels
IAppServer.
Remarque ScktSrvr.exe peut s’exécuter en tant qu’application de service NT. Recensez-le
dans le gestionnaire de services en le lançant avec l’option en ligne de
commande -install. Vous pouvez le dérecenser avec l’option en ligne de
commande -uninstall.
Avant de pouvoir utiliser une connexion Socket, le serveur d’applications doit
recenser sa disponibilité pour les clients utilisant une connexion Socket. Par
défaut, tous les nouveaux modules de données distants se recensent eux-mêmes
grâce à l’ajout d’un appel à EnableSocketTransport dans la méthode UpdateRegistry.
Vous pouvez supprimer cet appel pour interdire les connexions Socket à votre
serveur d’applications.
Remarque Des serveurs plus anciens n’ajoutant pas ce recensement, vous pouvez désactiver
la vérification que le serveur d’applications est recensé en désélectionnant dans
ScktSrvr.exe l’élément de menu Connexions|Objets recensés seulement.

14-10 Guide du développeur


Présentation de la technologie MIDAS

Tant qu’il n’a pas libéré une référence aux interfaces sur le serveur
d’applications, le protocole Sockets n’offre aucune protection sur le serveur
contre les défaillances système client. Tout en générant moins de trafic de
messages que DCOM (qui envoie périodiquement des messages), l’utilisation de
Sockets peut aboutir à la situation dans laquelle un serveur d’applications, non
conscient de la déconnexion du client, ne libère pas ses ressources.

Utilisation de connexions Web


HTTP vous permet de créer des clients pouvant communiquer avec un serveur
d’applications protégé par un “coupe-feu”. Les messages HTTP procurent un
accès contrôlé aux applications internes afin que vos applications client soient
distribuées largement en toute sécurité. Comme les connexions Socket, les
messages HTTP offre un plus petit dénominateur commun, que vous savez
disponible, pour établir des connexions au serveur d’applications. Pour plus
d’informations sur les messages HTTP, voir chapitre 29, “Création d’applications
serveur pour Internet”.
Au lieu d’instancier directement le module de données distant à partir du client
(comme cela se produit pour DCOM), les connexions basées sur HTTP utilisent
un serveur d’applications Web sur le serveur (httpsrvr.dll), qui accepte les
requêtes client et instancie le module de données distant à l’aide de COM. De ce
fait, on les appelle également connexions Web. Le composant connexion sur le
client et httpsrvr.dll sur le serveur sont responsables du tri des appels
IAppServer.
Les connexions Web peuvent tirer parti de la sécurité SSL apportée par
wininet.dll (une bibliothèque d’utilitaires internet qui s’exécute sur le système
client). Lorsque vous avez configuré le serveur Web sur le système serveur pour
qu’il exige une authentification, vous pouvez spécifier le nom d’utilisateur et le
mot de passe en utilisant les propriétés du composant de la connexion Web.
Comme autre mesure de sécurité, le serveur d’applications doit recenser sa
disponibilité pour les clients utilisant une connexion Web. Par défaut, tous les
nouveaux modules de données distants se recensent eux-mêmes grâce à l’ajout
d’un appel à EnableWebTransport dans la méthode UpdateRegistry. Vous pouvez
supprimer cet appel pour interdire les connexions Web à votre serveur
d’applications.
Les connexions Web peuvent bénéficier du regroupement des objets . Il permet à
votre serveur de créer un groupe limité d’instances des modules de données
distants accessibles aux requêtes client. En groupant les modules de données
distants, votre serveur utilise des ressources pour les modules de données et
pour les connexions aux bases de données uniquement lorsque cela est
nécessaire. Pour plus d’informations sur le regroupement des objets, voir
“Regroupement des modules de données distants” à la page 14-8.
Au contraire de ce qui se passe avec les autres composants connexion, vous ne
pouvez pas utiliser de rappels lorsque la connexion a été établie via HTTP.

Création d’applications multiniveaux 14-11


Construction d’une application multiniveau

Utilisation de OLEnterprise
OLEnterprise vous permet d’utiliser le courtier d’objets d’entreprise au lieu du
courtage côté client. Le courtier d’objets d’entreprise offre la répartition
équilibrée de la charge, le basculement de serveur et la transparence de
localisation.
Lorsque vous utilisez OLEnterprise, vous devez installer le runtime OLEnterprise
sur les systèmes client et serveur. Le runtime OLEnterprise gère le tri des appels
Automation et communique entre les systèmes client et serveur à l’aide d’appels
de procédures à distance (RPC). Pour plus d’informations, reportez-vous à la
documentation OLEnterprise.

Utilisation de connexions CORBA


CORBA vous permet d’intégrer vos applications de bases de données
multiniveaux à un environnement standardisé CORBA. Par exemple, les
composants client pour Java MIDAS s’appuient sur une connexion CORBA.
CORBA (et Java) étant accessible sur de multiples plates-formes, cela vous
permet d’écrire des applications MIDAS pour plusieurs plates-formes. Pour plus
d’informations sur l’utilisation de CORBA dans Delphi, voir chapitre 28,
“Ecriture d’applications CORBA”.
Grâce à CORBA, votre application bénéficie automatiquement de la répartition
équilibrée de la charge, de la transparence de localisation et du basculement de
serveur du logiciel runtime ORB. De plus, vous pouvez ajouter des hooks pour
tirer parti des autres services CORBA.

Construction d’une application multiniveau


Les étapes générales de création d’une application de bases de données
multiniveau sont les suivantes ;
1 Créez le serveur d’applications.
2 Recensez ou installez le serveur d’applications.
• Si le serveur d’applications utilise DCOM, HTTP, Sockets ou OLEnterprise
comme protocole de communication, il fait office de serveur Automation et
doit être recensé comme tout autre serveur ActiveX ou COM. Pour plus
d’informations sur le rencensement d’une application, voir “Recensement
d’une application comme serveur Automation” à la page 47-6.
• Si vous utilisez MTS, le serveur d’applications doit être une bibliothèque
active plutôt qu’un fichier .EXE. Comme tous les appels COM doivent
transiter par le proxy MTS, vous n’avez pas besoin de recenser le serveur
d’applications. Par contre, vous l’installez avec MTS. Pour plus
d’informations sur l’installation des bibliothèques avec MTS, voir
“Installation des objets MTS dans un paquet MTS” à la page 50-23.

14-12 Guide du développeur


Création du serveur d’applications

• Lorsque le serveur d’applications utilise CORBA, le recensement est


facultatif. Si vous souhaitez que les applications client utilisent la liaison
dynamique à votre interface, vous devez installer l’interface du serveur
dans le référentiel d’interfaces. De plus, si vous souhaitez que les
applications client démarrent le serveur d’applications lorsqu’il n’est pas
encore exécuté, ce dernier doit être recensé avec l’OAD (Object Activation
Daemon). Pour plus d’informations sur le recensement d’un serveur
CORBA, voir “Recensement d’interfaces serveur” à la page 28-9.
3 Créez une application client.
L’ordre de création est important. Vous devez créer et exécuter le serveur
d’applications avant de créer un client. Lors de la conception, vous pouvez alors
vous connecter au serveur d’applications pour tester votre client. Vous pouvez,
bien entendu, créer un client sans spécifier le serveur d’applications lors de la
conception et n’indiquer le nom du serveur qu’à l’exécution, mais cela ne vous
permet pas de voir si votre application fonctionne correctement et vous ne
pourrez pas choisir les serveurs et les fournisseurs à l’aide de l’inspecteur d’objets.
Remarque Si vous ne créez pas l’application client sur le même système que le serveur, et
si vous n’utilisez pas une connexion Web ou Socket, vous pouvez recenser ou
installer le serveur d’applications sur le système client. Ainsi, le composant
connexion détecte le serveur d’applications à la conception, ce qui vous permet
de choisir des noms de serveur et de fournisseur à partir d’une liste déroulante
de l’inspecteur d’objets. Si vous utilisez une connexion Web ou Socket, le
composant connexion lit le nom des serveurs recensés sur la machine serveur.

Création du serveur d’applications


La création d’un serveur d’applications est très similaire à la création de la plupart
des applications de bases de données. La principale différence réside dans le fait
que le serveur d’applications inclut un fournisseur d’ensembles de données.
Pour créer un serveur d’applications, démarrez un nouveau projet, enregistrez-le
et effectuez les étapes suivantes :
1 Ajoutez un nouveau module de données distant au projet. Dans le menu
principal, choisissez Fichier|Nouveau. Choisissez la page Multiniveaux de la
boîte de dialogue Nouveaux éléments et sélectionnez
• Module de données distant si vous créez un serveur COM Automation
auxquels les clients peuvent accéder à l’aide de DCOM, HTTP, Sockets ou
OLEnterprise ;
• Module de données MTS si vous créez une bibliothèque Active à laquelle
les clients peuvent accéder à l’aide de MTS. Les connexions peuvent être
établies à l’aide de DCOM, HTTP, Sockets ou OLEnterprise ;
• Module de données CORBA si vous créez un serveur CORBA.
Pour plus de détails sur la configuration d’un module de données distant, voir
“Configuration du module de données distant” à la page 14-15.

Création d’applications multiniveaux 14-13


Création du serveur d’applications

Remarque Les modules de données distants sont plus que de simples modules de
données. Le module de données distant CORBA fait office de serveur CORBA.
Les autres modules de données sont des objets COM Automation.
2 Placez sur le module de données les composants ensemble de données
appropriés, et configurez-les pour accéder au serveur de base de données.
3 Placez sur le module de données un composant TDataSetProvider pour chaque
ensemble de données. Ce fournisseur est nécessaire au courtage des demandes
client et à l’empaquetage des données.
4 Attribuez à la propriété DataSet de chaque composant fournisseur le nom de
l’ensemble de données devant être accessible. Vous pouvez définir d’autres
propriétés pour le fournisseur. Pour plus de détails sur la configuration d’un
fournisseur, voir chapitre 15, “Utilisation des composants fournisseur”.
5 Ecrivez du code pour le serveur d’applications afin d’implémenter les
événements, les règles d’entreprise partagées, les validations de données
partagées et la sécurité partagée. Vous pouvez étendre l’interface du serveur
d’applications afin d’offrir à l’application client d’autres possibilités pour appeler
le serveur. Pour plus d’informations sur l’extension de l’interface du serveur,
voir “Extension de l’interface du serveur d’applications” à la page 14-19.
6 Enregistrez, compilez et recensez ou installez le serveur d’applications.
• Lorsque le serveur d’applications utilise DCOM, HTTP, Sockets ou
OLEnterprise comme protocole de communication, il fait office de serveur
Automation et doit être recensé comme tout autre serveur ActiveX ou
COM. Pour plus d’informations sur le recensement d’une application, voir
“Recensement d’une application comme serveur Automation” à la
page 47-6.
• Si vous utilisez MTS, le serveur d’applications doit être une bibliothèque
active plutôt qu’un fichier .EXE. Comme tous les appels COM doivent
transiter par le proxy MTS, vous n’avez pas besoin de recenser le serveur
d’applications. Par contre, vous l’installez avec MTS. Pour plus
d’informations sur l’installation des bibliothèques avec MTS, voir
“Installation des objets MTS dans un paquet MTS” à la page 50-23.
• Lorsque le serveur d’applications utilise CORBA, le recensement est
facultatif. Si vous souhaitez que les applications client utilisent la liaison
dynamique à votre interface, vous devez installer l’interface du serveur
dans le référentiel d’interfaces. De plus, si vous souhaitez que les
applications client démarrent le serveur d’applications lorsqu’il n’est pas
encore exécuté, ce dernier doit être recensé avec l’OAD (Object Activation
Daemon). Pour plus d’informations sur le recensement d’un serveur
CORBA, voir “Recensement d’interfaces serveur” à la page 28-9.
7 Si votre serveur d’applications n’utilise pas DCOM, vous devez installer le
logiciel runtime qui reçoit les messages client, instancie le module de données
distant et trie les appels d’interface.
• Pour le protocole TCP/IP Sockets, il s’agit d’une application de répartition
de sockets, Scktsrvr.exe.

14-14 Guide du développeur


Création du serveur d’applications

• Pour les connexions HTTP, il s’agit de httpsrvr.dll, une dll ISAPI/NSAPI


qui doit être installée avec votre serveur Web.
• Pour OLEnterprise, il s’agit du runtime OLEnterprise.
• Pour CORBA, il s’agit du VisiBroker ORB.

Configuration du module de données distant


Lorsque vous configurez et exécutez un serveur d’applications, ce dernier
n’établit pas de connexion avec les applications client. C’est au contraire les
applications client qui maintiennent la connexion. L’application client utilise son
composant connexion pour établir une connexion avec le serveur d’applications,
qu’il utilise pour communiquer avec son fournisseur sélectionné. Tout ce
processus est automatique et vous n’avez pas besoin d’écrire du code pour gérer
les requêtes entrantes ni pour fournir les interfaces.
Lorsque vous créez le module de données distant, vous devez fournir certaines
informations indiquant comment il répond aux requêtes client. Ces informations
varient avec le type de module de données distant. Voir “Structure du serveur
d’applications” à la page 14-5 pour plus d’informations sur le type de module de
données distant nécessaire.

Configuration de TRemoteDataModule
Pour ajouter un composant TRemoteDataModule dans votre application, choisissez
Fichier|Nouveau et sélectionnez Module de données distant dans la page Multi-
niveaux de la boîte de dialogue Nouveaux éléments. L’expert Module de
données distant apparaît.
Vous devez fournir un nom de classe pour votre module de données distant. Il
s’agit du nom de base d’un descendant de TRemoteDataModule que votre
application crée. Il s’agit aussi du nom de base de l’interface de cette classe. Par
exemple, si vous spécifiez le nom de classe MyDataServer, l’expert crée une
nouvelle unité en déclarant TMyDataServer, un descendant de
TRemoteDataModule, qui implémente IMyDataServer, un descendant de IAppServer.
Remarque Vous pouvez ajouter vos propres propriétés et méthodes à la nouvelle interface.
Pour plus d’informations, voir “Extension de l’interface du serveur
d’applications” à la page 14-19.
Si vous créez une bibliothèque de liaison dynamique (Active Library), vous
devez spécifier le modèle threading dans l’expert Module de données distant.
Vous pouvez choisir Thread Unique, Thread Appartement, Thread Libre ou Les
deux.
• Si vous choisissez Thread Unique, COM fait en sorte qu’une seule requête
client soit traitée à un moment donné. Aucune requête client ne peut entrer en
conflit avec une autre.

Création d’applications multiniveaux 14-15


Création du serveur d’applications

• Si vous choisissez Thread Appartement, COM fait en sorte que toute instance
de votre module de données distant traite une seule requête à un moment
donné. Lorsque vous écrivez du code dans une bibliothèque Thread
Appartement, vous devez prévenir les conflits de thread si vous utilisez des
variables globales ou des objets non contenus dans le module de données
distant. C’est le modèle recommandé si vous utilisez des ensembles de
données BDE. (Veuillez noter que vous aurez besoin d’un composant session
dont la propriété AutoSessionName vaut True pour gérer les problèmes de
threading sur les ensembles de données BDE)
• Si vous choisissez Thread Libre, votre application peut recevoir des requêtes
client simultanées sur plusieurs threads. Vous devez faire en sorte que votre
application soit compatible avec les threads. Comme plusieurs clients peuvent
simultanément accéder à votre module de données distant, vous devez
protéger vos données d’instance (propriétés, objets contenus, etc.) ainsi que les
variables globales. C’est le modèle recommandé si vous utilisez des ensembles
de données ADO.
• Si vous choisissez Les deux, votre bibliothèque fonctionne de la même façon
que lorsque vous choisissez Thread Libre, à la différence que tous les rappels
(appels vers les interfaces client) sont automatiquement sérialisés.
Si vous créez un fichier .EXE, vous devez spécifier le type d’instanciation à
utiliser. Vous pouvez choisir Instance unique ou Instance multiple (l’instanciation
interne ne s’applique que si le code client fait partie du même espace de
processus.)
• Si vous choisissez Instance unique, chaque connexion client lance sa propre
instance du fichier exécutable. Ce processus instancie une seule instance du
module de données distant, qui est dédié à la connexion client.
• Si vous choisissez Instance multiple, une seule instance de l’application
(processus) instancie tous les modules de données distants créés pour les
clients. Chaque module de données distant est dédié à une seule connexion
client, mais ils partagent tous le même espace de processus.

Configuration de TMTSDataModule
Pour ajouter un composant TMTSDataModule dans votre application, choisissez
Fichier|Nouveau et sélectionnez Module de données MTS dans la page Multi-
niveaux de la boîte de dialogue Nouveaux éléments. L’expert Module de
données MTS apparaît.
Vous devez fournir un nom de classe pour votre module de données distant. Il
s’agit du nom de base d’un descendant de TMTSDataModule que votre
application crée. Il s’agit aussi du nom de base de l’interface de cette classe. Par
exemple, si vous spécifiez le nom de classe MyDataServer, l’expert crée une
nouvelle unité en déclarant TMyDataServer, un descendant de TMTSDataModule,
qui implémente IMyDataServer, un descendant de IAppServer.
Remarque Vous pouvez ajouter vos propres propriétés et méthodes à votre nouvelle
interface. Pour plus d’informations, voir “Extension de l’interface du serveur
d’applications” à la page 14-19.

14-16 Guide du développeur


Création du serveur d’applications

Les applications MTS sont toujours des bibliothèques de liaison dynamique


(Active Library), vous devez spécifier le modèle threading dans l’expert Module
de données MTS. Choisissez Unique, Appartement, Libre ou Les deux.
• Si vous choisissez Unique, MTS fait en sorte qu’une seule requête client soit
traitée à un moment donné. Aucune requête client ne peut entrer en conflit
avec une autre.
• Si vous choisissez Appartement ou Libre, vous obtenez la même chose : MTS
fait en sorte que toute instance de votre module de données distant traite une
seule requête à un moment donné, mais les appels n’utilisent pas toujours le
même thread. Vous ne pouvez pas utiliser de variables de thread car rien ne
garantit que les appels successifs de l’instance du module de données distant
utilisent le même thread. Vous devez prévenir les conflits de thread si vous
utilisez des variables globales ou des objets non contenus dans le module de
données distant. Au lieu d’utiliser des variables globales, vous pouvez utiliser
le gestionnaire de propriétés partagées. Pour plus d’informations sur le
gestionnaire de propriétés partagées, voir “Gestionnaire de propriétés
partagées” à la page 50-13.
• Si vous choisissez Les deux, MTS appelle l’interface du module de données
distant de la même façon que lorsque vous choisissez Appartement ou Libre.
Toutefois, tous les rappels réalisés en direction des applications clients sont
automatiquement sérialisés afin qu’aucun n’entre en conflit avec un autre.
Remarque Les modèles Appartement et Libre sous MTS diffèrent des modèles
correspondants sous DCOM.
Vous devez aussi spécifier les attributs des transactions MTS de votre module de
données distant. Vous pouvez choisir parmi les options suivantes :
• Requiert une transaction. Lorsque vous sélectionnez cette option, chaque fois
qu’un client utilise l’interface de votre module de données distant, l’appel est
exécuté dans le contexte d’une transaction MTS. Si l’appelant fournit une
transaction, il est inutile qu’une nouvelle transaction soit créée.
• Requiert une nouvelle transaction. Lorsque vous sélectionnez cette option,
chaque fois qu’un client utilise l’interface de votre module de données distant,
une nouvelle transaction est automatiquement créée pour l’appel.
• Supporte les transactions. Lorsque vous sélectionnez cette option, votre
module de données distant peut être utilisé dans le contexte d’une transaction
MTS, mais l’appelant doit fournir la transaction lorsqu’il appelle l’interface.
• Ne supporte pas les transactions. Lorsque vous sélectionnez cette option, votre
module de données distant ne peut pas être utilisé dans le contexte de
transactions MTS.

Configuration de TCorbaDataModule
Pour ajouter un composant TCorbaDataModule dans votre application, choisissez
Fichier|Nouveau et sélectionnez Module de données CORBA dans la page
Multi-niveau de la boîte de dialogue Nouveaux éléments. L’expert Module de
données CORBA apparaît.

Création d’applications multiniveaux 14-17


Création du serveur d’applications

Vous devez fournir un nom de classe pour votre module de données distant. Il
s’agit du nom de base d’un descendant de TCorbaDataModule que votre
application crée. Il s’agit aussi du nom de base de l’interface de cette classe. Par
exemple, si vous spécifiez le nom de classe MyDataServer, l’expert crée une
nouvelle unité en déclarant TMyDataServer, un descendant de TCorbaDataModule,
qui implémente IMyDataServer, un descendant de IAppServer.
Remarque Vous pouvez ajouter vos propres propriétés et méthodes à votre nouvelle
interface. Pour plus d’informations sur l’ajout d’éléments à l’interface de votre
module de données, voir “Extension de l’interface du serveur d’applications” à la
page 14-19.
L’expert module de données CORBA vous permet de spécifier comment votre
application serveur crée les instances du module de données distant. Vous
pouvez choisir entre le mode partagé ou instance par client.
• Lorsque vous choisissez le mode partagé, votre application crée une instance
unique du module de données distant qui gère toutes les requêtes client. Ce
modèle est traditionnellement utilisé dans le développement CORBA.
• Lorsque vous choisissez le mode une instance par client, une nouvelle instance
du module de données distant est créée pour chaque connexion client. Cette
instance persiste jusqu’à ce que le délai d’inactivité soit écoulé sans message
de la part du client. Cela permet certes au serveur de libérer les instances
lorsqu’elles ne sont plus utilisées par les clients, mais présente le risque que le
serveur soit libéré prématurément si le client n’utilise pas l’interface du
serveur pendant une longue période.
Remarque Contrairement au modèle d’instanciation des serveurs COM, où est déterminé le
nombre d’instances du processus qui sont exécutées, dans le modèle CORBA,
l’instanciation détermine le nombre d’instances de votre objet qui sont créées.
Elles sont toutes créées dans une seule instance de l’exécutable serveur.
Outre le modèle d’instanciation, vous devez spécifier le modèle threading dans
l’expert Module de données CORBA. Vous pouvez choisir Monothread ou
Multithread.
• Si vous choisissez Monothread, chaque instance du module de données distant
est assurée de ne recevoir qu’une requête client à un moment donné. Vous
pouvez accéder en sécurité aux objets contenus dans votre module de données
distant. Toutefois, vous devez prévenir les conflits de thread lorsque vous
utilisez des variables globales ou des objets non contenus dans le module de
données distant.
• Si vous choisissez Multithread, chaque connexion client dispose de son
propre thread. Toutefois, votre application peut être appelée par plusieurs
clients simultanément, chacun sur un thread différent. Vous devez prévenir
l’accès simultané aux données d’instance ainsi qu’à la mémoire globale.
L’écriture de serveurs multithreads est délicate lorsque vous utilisez une
instance partagée du module de données distant car vous devez protéger
toute l’utilisation des objets contenus dans le module.

14-18 Guide du développeur


Création du serveur d’applications

Création d’un fournisseur de données pour le serveur


d’applications
Chaque module de données distant d’un serveur d’applications contient un ou
plusieurs composants fournisseur. Chaque ensemble de données client utilise un
fournisseur spécifique, qui agit comme une passerelle entre l’ensemble de
données client et les données qu’il représente. Un composant fournisseur
(TDataSetProvider) se charge de constituer les paquets de données qu’il envoie
aux clients et d’appliquer les mises à jour reçus de ces derniers.
La logique se rapportant aux données du serveur d’applications est en majeure
partie traitée par les composants fournisseur contenus dans le module de
données distant. Les gestionnaires d’événements répondant aux requêtes client
implémentent vos règles de gestion et de données, alors que les propriétés du
composant fournisseur contrôlent les informations contenues dans les paquets de
données. Voir chapitre 15, “Utilisation des composants fournisseur” pour plus de
détails sur l’utilisation d’un composant fournisseur pour contrôler l’interaction
avec les applications client.

Extension de l’interface du serveur d’applications


Les applications client interagissent avec le serveur d’applications en créant, ou
en s’y connectant, une instance du module de données distant. Elles utilisent son
interface comme base de toute communication avec le serveur d’applications.
Vous pouvez effectuer un ajout à l’interface de votre module de données distant
afin d’améliorer la prise en charge de vos applications client. Cette interface est
un descendant de IAppServer ; elle est automatiquement créée par l’expert
lorsque vous créez le module de données distant.
Pour effectuer un ajout à l’interface du module de données distant, vous pouvez
• Choisir la commande Ajouter à l’interface dans le menu Edition de l’EDI.
Indiquez si vous ajoutez une procédure, une fonction ou une propriété et
entrez la syntaxe. Lorsque vous cliquez sur OK, vous vous retrouvez dans
l’éditeur de code sur l’implémentation du nouveau membre de votre interface.
• Utiliser l’éditeur de bibliothèque de types. Sélectionnez l’interface de votre
serveur d’applications dans l’éditeur de bibliothèque de types et cliquez sur le
bouton d’outil correspondant au type de membre d’interface (méthode ou
propriété) que vous ajoutez. Nommez votre membre d’interface dans la page
Attributs, spécifiez les paramètres et le type dans la page Paramètres puis
rafraîchissez la bibliothèque de types. Pour plus d’informations sur l’utilisation
de l’éditeur de bibliothèque de types, voir chapitre 49, “Utilisation des
bibliothèques de types”. Notez que de nombreuses fonctionnalités que vous
pouvez spécifier dans l’éditeur de bibliothèque de types (comme l’aide
contextuelle, la version, etc.) ne s’appliquent pas aux interfaces CORBA.
Toutes les valeurs que vous spécifiez pour ces dernières dans l’éditeur de
bibliothèque de types sont ignorées.

Création d’applications multiniveaux 14-19


Création du serveur d’applications

Le comportement de Delphi lorsque vous ajoutez de nouvelles entrées à l’interface


diffère selon que vous créez un serveur basé sur COM (TRemoteDataModule ou
TMTSDataModule) ou un serveur CORBA (TCorbaDataModule).
• Lorsque vous ajoutez une interface COM, vos modifications sont ajoutées au
code source de votre unité et au fichier de la bibliothèque de types (.TLB).
• Lorsque vous effectuez un ajout à l’interface CORBA, vos modifications sont
répercutées dans le code source de votre unité et dans l’unité _TLB
automatiquement générée. L’unité _TLB est ajoutée à la clause uses de votre
unité. Vous devez ajouter cette unité à la clause uses dans votre application
client si sous souhaitez tirer parti de la liaison anticipée. De plus, vous pouvez
enregistrer un fichier .IDL à partir de l’éditeur de bibliothèque de types à
l’aide de la commande Fichier|Enregistrer sous. Le fichier .IDL est requis
pour recenser l’interface dans le référentiel d’interfaces et dans le démon
d’activation d’objets.
Remarque Vous devez enregistrer explicitement le fichier TLB en choisissant Rafraîchir dans
l’éditeur de bibliothèque de types et en sauvegardant ensuite les modifications
depuis l’EDI.
Une fois que vous avez effectué un ajout à l’interface de votre module de
données distant, localisez les propriétés et les méthodes ajoutées à
l’implémentation de votre module de données distant. Ajoutez du code pour
terminer cette implémentation.
Les applications client appellent les extensions de votre interface à l’aide de la
propriété AppServer de leur composant connexion. Pour plus d’informations sur
la procédure à suivre, voir “Appel des interfaces serveur” à la page 14-27.

Ajout de rappels à l’interface du serveur d’applications


Vous pouvez faire que le serveur d’applications appele votre application client
en introduisant un rappel. Pour ce faire, l’application client passe une interface à
l’une des méthodes du serveur d’applications, et le serveur d’applications appelle
ensuite cette méthode. Cependant, si vos extensions à l’interface du module de
données distant comprennent des rappels, vous ne pouvez pas utiliser de
connexion HTTP. TWebConnection ne supporte pas les rappels. Si vous utilisez
une connexion de type socket, les applications client doivent indiquer si elles
utilisent ou non les rappels par la définition de la propriété SupportCallbacks.
Tous les autres types de connexions supportent automatiquement les rappels.

Extension de l’interface du serveur d’applications lors de l’utilisation de


MTS
Lors de l’utilisation des transactions ou de l’activation “juste à temps” sous MTS,
vous devez être certain que toutes les nouvelles méthodes appellent SetComplete
pour signaler à MTS quand elles sont terminées. Cela permet l’achèvement des
transactions et ensuite la désactivation du module de données distant. De plus, il
vous est impossible de renvoyer de vos nouvelles méthodes une valeur qui fasse
communiquer le client directement avec les objets ou les interfaces sur le serveur

14-20 Guide du développeur


Création de l’application client

d’applications. C’est parce que toute communication ne passant pas par


l’interface du module de données distant court-circuite le proxy MTS, ce qui
peut invalider les transactions. Si vous utilisez un module de données MTS sans
état, court-circuiter le proxy MTS peut entraîner des plantages car vous ne
pouvez garantir que le module de données distant est actif.

Création de l’application client


A bien des égards, la création d’une application client multiniveau est similaire à
la création d’un client traditionnel à double niveau. Les différences majeures
résident dans le fait qu’un client multiniveau utilise
• Un composant connexion pour établir un canal de communication avec le
serveur d’applications.
• Un ou plusieurs composants TClientDataSet pour établir la liaison avec un
fournisseur de données sur le serveur d’applications. Les contrôles orientés
données sur le client sont connectés à ces ensembles de données client par
l’intermédiaire de composants source de données au lieu de composants
TTable, TQuery, TStoredProc ou TADODataSet.
Pour créer une application client multiniveau, démarrez un nouveau projet et
effectuez les étapes suivantes :
1 Ajoutez un nouveau module de données au projet.
2 Placez un composant connexion sur le module de données. Le type de
composant connexion que vous ajoutez dépend du protocole de
communication que vous souhaitez utiliser. Voir “Structure de l’application
client” à la page 14-4 pour plus de détails.
3 Initialisez les propriétés sur votre composant connexion pour spécifier le
serveur d’applications avec lequel il doit établir une connexion. Pour plus
d’informations sur la configuration du composant connexion, voir “Connexion
au serveur d’applications” à la page 14-22.
4 Initialisez les autres propriétés du composant connexion selon les besoins de
votre application. Par exemple, vous pouvez initialiser la propriété ObjectBroker
pour permettre au composant connexion de choisir dynamiquement parmi
plusieurs serveurs. Pour plus d’informations sur l’utilisation des composants
connexion, voir “Gestion des connexions serveur” à la page 14-26
5 Placez autant de composants TClientDataSet que nécessaire sur le module de
données, et initialisez la propriété RemoteServer pour chaque composant avec
le nom du composant connexion placé lors de l’étape 2. Pour une présentation
détaillée des ensembles de données client, voir chapitre 24, “Création et
utilisation d’un ensemble de données client”
6 Initialisez la propriété ProviderName de chaque composant TClientDataSet. Si
votre composant connexion est connecté au serveur d’applications lors de la
conception, vous pouvez choisir un fournisseur disponible dans la liste
déroulante de la propriété ProviderName.

Création d’applications multiniveaux 14-21


Création de l’application client

7 Créez l’application client ainsi que vous le feriez pour toute autre application
de bases de données. Vous voudrez probablement utiliser les fonctions
spéciales des ensembles de données client qui interagissent avec les
composants fournisseur sur le serveur d’applications. Elles sont décrites par
“Utilisation d’un ensemble de données client avec un fournisseur de données”
à la page 24-16.

Connexion au serveur d’applications


Pour établir et maintenir une connexion avec un serveur d’applications, une
application client utilise un ou plusieurs composants connexion. Ces composants
se trouvent sur la page MIDAS de la palette des composants.
Utilisez un composant connexion pour
• Identifier le protocole utilisé pour communiquer avec le serveur d’applications.
Chaque type de composant connexion représente un protocole de
communication différent. Voir “Sélection d’un protocole de connexion” à la
page 14-9 pour plus de détails sur les avantages et les limites des protocoles
disponibles.
• Indiquer comment localiser la machine serveur. Les détails d’identification de
la machine serveur varient selon le protocole.
• Identifier le serveur d’applications sur la machine serveur.
Si vous n’utilisez pas CORBA, identifiez le serveur à l’aide de la propriété
ServerName ou ServerGUID. ServerName identifie le nom de base de la classe
que vous spécifiez lorsque vous créez le module de données distant sur le
serveur d’applications. Voir “Configuration du module de données distant” à
la page 14-15 pour plus de détails sur la spécification de cette valeur sur le
serveur. Si le serveur est recensé ou installé sur la machine client, ou si le
composant connexion est connecté à la machine serveur, vous pouvez
initialiser la propriété ServerName lors de la conception à partir d’une liste
déroulante dans l’inspecteur d’objets. ServerGUID spécifie le GUID de
l’interface du module de données distant. Vous pouvez rechercher cette valeur
à l’aide de l’éditeur de bibliothèque de types.
Si vous utilisez CORBA, identifiez le serveur à l’aide de la propriété
RepositoryID. RepositoryID spécifie l’Id de référentiel de l’interface usine du
serveur d’applications, qui apparaît comme troisième argument dans l’appel
de TCorbaVCLComponentFactory.Create, automatiquement ajouté à la section
d’initialisation de l’unité d’implémentation du serveur CORBA. Vous pouvez
aussi initialiser cette propriété sur le nom de base de l’interface du module de
données CORBA (la même chaîne que la propriété ServerName des autres
composants connexion) pour qu’elle soit automatiquement convertie en l’Id de
référentiel approprié.
• Gérer les connexions serveur. Les composants connexion peuvent être utilisés
pour créer ou abandonner des connexions et pour appeler des interfaces de
serveur d’applications.

14-22 Guide du développeur


Création de l’application client

Généralement, le serveur d’applications et l’application client se trouvent sur des


machines différentes. Mais même si le serveur réside sur la même machine que
l’application client (par exemple, pendant la construction et le test de toute
l’application multiniveau), vous pouvez utiliser le composant connexion pour
identifier le serveur d’applications par son nom, spécifier une machine serveur et
utiliser l’interface du serveur d’applications.

Spécification d’une connexion à l’aide de DCOM


Lorsque vous utilisez DCOM pour communiquer avec le serveur d’applications,
l’application cliente inclut un composant TDCOMConnection pour s’y connecter.
TDCOMConnection utilise la propriété ComputerName pour identifier la machine
sur laquelle réside le serveur.
Lorsque ComputerName est vierge, le composant connexion DCOM considère que
le serveur d’applications réside sur la machine client ou qu’il possède une entrée
de registre système. Lorsque vous utilisez DCOM et que le serveur réside sur
une machine différente de celle du client, vous devez fournir ComputerName si
vous ne fournissez pas d’entrée de registre système pour le serveur
d’applications sur le client.
Remarque Même lorsqu’il existe une entrée de registre système pour le serveur
d’applications, vous pouvez spécifier ComputerName pour écraser cette entrée. Cela
peut être particulièrement utile pendant le développement, le test et le débogage.
Si votre application client peut choisir parmi plusieurs serveurs, vous pouvez
utiliser la propriété ObjectBroker au lieu de spécifier la valeur de ComputerName.
Pour plus d’informations, voir “Courtage de connexions” à la page 14-26.
Si vous fournissez le nom d’un ordinateur hôte ou d’un serveur introuvable, le
composant connexion DCOM déclenche une exception lorsque vous essayez
d’ouvrir la connexion.

Spécification d’une connexion à l’aide de sockets


Vous pouvez établir une connexion à un serveur d’applications à l’aide de
sockets depuis n’importe quelle machine disposant d’une adresse TCP/IP. Cette
méthode présente l’avantage de pouvoir s’appliquer à davantage de machines,
mais ne permet pas l’utilisation des protocoles de sécurité. Lorsque vous utilisez
des sockets, incluez un composant TSocketConnection pour la connexion au
serveur d’applications.
TSocketConnection identifie la machine serveur à l’aide de l’adresse IP ou du nom
d’hôte du système serveur, et du numéro de port du programme de répartition de
sockets (Scktsrvr.exe) exécuté sur la machine serveur. Pour plus d’informations sur
les adresses IP et les valeurs de port, voir “Description des sockets” à la page 30-3.
Trois propriétés de TSocketConnection spécifient ces informations :
• Address spécifie l’adresse IP du serveur.
• Host spécifie le nom d’hôte du serveur.
• Port spécifie le numéro de port du programme de répartition de sockets sur le
serveur d’applications.

Création d’applications multiniveaux 14-23


Création de l’application client

Address et Host s’excluent l’une l’autre. Initialiser la valeur de l’une désinitialise


la valeur de l’autre. Pour plus d’informations sur la propriété à utiliser, voir
“Description des hôtes” à la page 30-4.
Si votre application client peut choisir parmi plusieurs serveurs, vous pouvez
utiliser la propriété ObjectBroker au lieu de spécifier une valeur pour Address ou
Host. Pour plus d’informations, voir “Courtage de connexions” à la page 14-26.
Par défaut, la valeur de Port est 211, c’est le numéro de port par défaut des
programmes de répartition de sockets fournis avec Delphi. Si le répartiteur de
sockets a été configuré pour utiliser un port différent, initialisez la propriété Port
en fonction de cette valeur.
Remarque Vous pouvez configurer le port du répartiteur de sockets en cliquant avec le
bouton droit sur l’icône du serveur de socket Borland et en choisissant Propriétés.
Bien que les connexions socket ne permettent pas l’utilisation des protocoles de
sécurité, vous pouvez personnaliser la connexion socket en ajoutant votre propre
crytage. Pour cela, créez et recensez un objet COM supportant l’interface
IDataIntercept. C’est une interface de cryptage et de décryptage des données.
Ensuite, attribuez le GUID de l’objet COM à la propriété InterceptGUID du
composant connexion socket. Enfin, cliquez avec le bouton droit sur l’icône du
serveur de socket Borland, choisissez Propriétés et, dans la page des propriétés,
définissez GUID Intercepteur par le même GUID. Ce mécanisme peut servir
également à la compression et à la décompression des données.

Spécification d’une connexion à l’aide de HTTP


Vous pouvez établir une connexion à un serveur d’applications à l’aide de HTTP
depuis n’importe quelle machine disposant d’une adresse TCP/IP. Au contraire
des sockets, HTTP vous permet de bénéficier de la sécurité SSL et de communiquer
avec un serveur protégé par un coupe-feu. Lorsque vous utilisez HTTP, incluez un
composant TWebConnection pour la connexion au serveur d’applications.
Le composant connexion Web établit une connexion vers l’application serveur
Web (httpsrvr.dll), qui à son tour communique avec le serveur d’applications.
TWebConnection localise httpsrvr.dll en utilisant une URL (Uniform Resource
Locator). L’URL spécifie le protocole (http ou, si vous utilisez la sécurité SSL,
https), le nom d’hôte de la machine exécutant le serveur Web et httpsrvr.dll,
ainsi que le chemin d’accès à l’application serveur Web (Httpsrvr.dll). Spécifiez
cette valeur avec la propriété URL.
Remarque Lorsque vous utilisez TWebConnection, wininet.dll doit être installé sur la
machine client. Si vous avez IE3 ou une version supérieure, wininet.dll se trouve
dans le répertoire système de Windows.
Si le serveur Web nécessite une authentification, ou si vous utilisez un serveur
proxy qui demande une authentification, vous devez définir la valeur des
propriétés UserName et Password pour que le composant connexion établisse la
connexion.
Si votre application client peut choisir parmi plusieurs serveurs, vous pouvez
utiliser la propriété ObjectBroker au lieu de spécifier la valeur de l’URL. Pour plus
d’informations, voir “Courtage de connexions” à la page 14-26.

14-24 Guide du développeur


Création de l’application client

Spécification d’une connexion à l’aide de OLEnterprise


Lorsque vous utilisez OLEnterprise pour communiquer avec le serveur
d’applications, les applications client doivent inclure un composant
TOLEnterpriseConnection pour s’y connecter. Lorsque vous utilisez OLEnterprise,
vous pouvez soit vous connecter directement à la machine serveur, soit utiliser le
courtier d’objets d’entreprise.
• Pour utiliser OLEnterprise sans passer par un courtier d’objets d’entreprise,
initialisez la propriété ComputerName sur le nom de la machine serveur, à
l’image de la propriété ComputerName pour une connexion DCOM.
• Pour utiliser les services de répartition équilibrée de la charge et de
basculement de serveur du courtier d’objets d’entreprise, initialisez la
propriété BrokerName sur le nom du courtier d’objets d’entreprise.
ComputerName et BrokerName s’excluent l’une l’autre. Initialiser la valeur de l’une
désinitialise la valeur de l’autre.
Pour plus d’informations sur l’utilisation de OLEnterprise, consultez la
documentation OLEnterprise.

Spécification d’une connexion à l’aide de CORBA


Seule la propriété RepositoryID est nécessaire pour spécifier une connexion
CORBA. En effet, sur le réseau local, un agent intelligent localise
automatiquement un serveur disponible pour votre client CORBA.
Toutefois, vous pouvez limiter le nombre de serveurs auxquels votre application
client peut se connecter à l’aide des autres propriétés du composant connexion
CORBA. Si vous souhaitez spécifier une machine serveur particulière plutôt que
laisser CORBA Smart Agent en localiser une de disponible, utilisez la propriété
HostName ~!Alink(TCorbaConnection_HostName,1) JMP~>. Si plusieurs instances
d’objets implémentent votre interface serveur, vous pouvez spécifier l’objet que
vous souhaitez utiliser en initialisant la propriété ObjectName.
Le composant TCorbaConnection obtient une interface vers le module de données
CORBA sur le serveur d’applications de deux manières :
• Si vous utilisez la liaison anticipée (statique), vous devez ajouter le fichier
_TLB.pas (généré par l’éditeur de bibliothèque de types) à votre application
client. La liaison anticipée est fortement recommandée pour la vérification du
type à la compilation et parce qu’elle est beaucoup plus rapide que la liaison
tardive (dynamique).
• Si vous utilisez la liaison tardive (dynamique), l’interface doit être recensée
dans le référentiel d’interfaces. Pour plus d’informations sur le recensement
d’une interface dans le référentiel d’interfaces, voir “Ecriture de clients
CORBA” à la page 28-13.
Pour une comparaison des liaisons anticipée et tardive, voir “Appel des
interfaces serveur” à la page 14-27.

Création d’applications multiniveaux 14-25


Création de l’application client

Courtage de connexions
Si votre application client peut choisir parmi plusieurs serveurs, vous pouvez
utiliser un courtier d’objets pour localiser un système serveur disponible. Le
courtier d’objets gère une liste de serveurs disponibles pour le composant
connexion. Lorsque le composant connexion a besoin de se connecter à un
serveur d’applications, il demande au courtier d’objets un nom d’ordinateur (ou
une adresse IP, un nom d’hôte, une URL). Le courtier fournit un nom puis le
composant connexion établit la connexion. Si le nom fourni ne fonctionne pas
(par exemple si le serveur n’est pas opérationnel), le courtier fournit un autre
nom et répète l’opération jusqu’à ce que la connexion soit établie.
Une fois que le composant connexion a établi une connexion avec un nom fourni
par le courtier, il enregistre ce nom en tant que valeur de la propriété appropriée
(ComputerName, Address, Host, RemoteHost ou URL). Si le composant connexion
ferme la connexion puis a besoin de l’ouvrir à nouveau, il utilise cette valeur de
propriété et ne demande un nouveau nom au courtier que si la connexion
échoue.
Pour utiliser un courtier d’objets, spécifiez la propriété ObjectBroker de votre
composant connexion. Lorsque la propriété ObjectBroker est initialisée, le
composant connexion n’enregistre pas la valeur de ComputerName, Address, Host,
RemoteHost ou URL.
Remarque N’utilisez pas la propriété ObjectBroker avec des connexions OLEnterprise ni avec
des connexions CORBA. Chacun de ces deux protocoles a son propre service de
courtage.

Gestion des connexions serveur


La fonction principale des composants connexion est de localiser le serveur
d’applications et de s’y connecter. Comme ils gèrent les connexions serveur, ils
vous permettent également d’appeler les méthodes de l’interface du serveur
d’applications.

Connexion au serveur
Pour localiser le serveur d’applications et vous y connecter, vous devez d’abord
initialiser les propriétés du composant connexion pour identifier le serveur
d’applications. Ce processus est décrit dans “Connexion au serveur
d’applications” à la page 14-22. De plus, avant d’ouvrir la connexion, tous les
ensembles de données client qui utilisent le composant connexion pour
communiquer avec le serveur d’applications doivent l’indiquer en initialisant leur
propriété RemoteServer.
La connexion est automatiquement ouverte lorsque les ensembles de données
client essaient d’accéder au serveur d’applications. Par exemple, l’initialisation à
True de la propriété Active de l’ensemble de données client ouvre la connexion,
pour autant que la propriété RemoteServer ait été définie.

14-26 Guide du développeur


Création de l’application client

Si vous ne liez aucun ensemble de données client au composant connexion, vous


pouvez ouvrir la connexion en initialisant à True la propriété Connected du
composant connexion.
Avant d’établir une connexion à un serveur d’applications, un composant
connexion génère un événement BeforeConnect. Vous pouvez exécuter des actions
particulières avant de vous connecter en les codant dans un gestionnaire
BeforeConnect. Après avoir établi une connexion, le composant connexion génère
un événement AfterConnect où vous pouvez également exécuter toute action
nécessaire.

Fermeture ou changement de connexion serveur


Un composant connexion ferme une connexion à un serveur d’applications dans
les circonstances suivantes :
• Quand vous initialisez la propriété Connected à False.
• Quand vous libérez le composant connexion. Un objet connexion est libéré
automatiquement quand un utilisateur ferme l’application client.
• Quand vous modifiez les propriétés qui identifient le serveur d’applications
(ServerName, ServerGUID, ComputerName, etc.). La modification de ces
propriétés vous permet de basculer entre les serveurs d’applications
disponibles lors de l’exécution. Le composant connexion ferme la connexion en
cours et en établit une nouvelle.
Remarque Au lieu d’utiliser un seul composant connexion pour basculer entre les serveurs
d’applications disponibles, une application client peut disposer de plusieurs
composants connexion, chacun d’eux étant connecté à un serveur d’applications
particulier.
Avant de fermer une connexion, un composant connexion appelle
automatiquement son gestionnaire d’événement BeforeDisconnect, s’il existe. Pour
exécuter des actions particulières avant la déconnexion, écrivez un gestionnaire
BeforeDisconnect. De même, après la fermeture de la connexion, le gestionnaire
d’événement AfterDisconnect est appelé. Pour exécuter des actions particulières
après la déconnexion, écrivez un gestionnaire AfterDisconnect.

Appel des interfaces serveur


Les applications n’ont pas besoin d’appeler l’interface IAppServer directement, car
les appels appropriés sont automatiquement réalisés lorsque vous utilisez les
propriétés et méthodes de l’ensemble de données client. Cependant, alors qu’il
n’est pas nécessaire de travailler directement avec l’interface IAppServer, vous
pouvez avoir ajouté vos propres extensions à l’interface du module de données
distant. Lorsque vous étendez l’interface du serveur d’applications , vous devez
être en mesure d’appeler ces extensions à l’aide de la connexion créée par votre
composant connexion. A cet effet, vous pouvez utiliser la propriété AppServer du
composant connexion. Pour plus d’informations sur l’extension de l’interface du
serveur d’applications, voir “Extension de l’interface du serveur d’applications” à
la page 14-19.

Création d’applications multiniveaux 14-27


Création de l’application client

AppServer est un Variant qui représente l’interface du serveur d’applications.


Vous pouvez appeler une méthode d’interface à l’aide de AppServer en écrivant
une instruction telle que
MyConnection.AppServer.SpecialMethod(x,y);
Toutefois, cette technique offre une liaison tardive (dynamique) de l’appel
d’interface. En raison de cela, l’appel de la procédure SpecialMethod n’est pas liée
avant l’exécution lorsque l’appel est exécuté. La liaison tardive est très souple
mais son utilisation vous prive de nombreux avantages tels que code insight et
la vérification de type. De plus, la liaison tardive est plus lente que la liaison
anticipée car le compilateur génère des appels supplémentaires vers le serveur
pour configurer les appels d’interface avant de les réaliser.
Lorsque vous utilisez DCOM ou CORBA comme protocole de communication,
vous pouvez utiliser la liaison anticipée d’appels AppServer. Utilisez l’opérateur
as pour affecter à la variable AppServer le descendant IAppServer que vous avez
créé lorsque vous avez créé le module de données distant. Par exemple :
with MyConnection.AppServer as IMyAppServer do
SpecialMethod(x,y);
Pour utiliser la liaison anticipée sous DCOM, la bibliothèque de types du serveur
doit être recensée sur la machine client. Vous pouvez utiliser TRegsvr.exe, fourni
avec Delphi, pour recenser la bibliothèque de types.
Remarque Reportez-vous à la démo TRegSvr (qui offre le source de TRegsvr.exe) pour un
exemple de recensement de la bibliothèque de types par programmation.
Pour utiliser la liaison anticipée avec CORBA, vous devez ajouter à votre projet
l’unité _TLB générée par l’éditeur de bibliothèque de types. Pour ce faire, ajoutez
cette unité à la clause uses de votre unité.
Lorsque vous utilisez TCP/IP ou OLEnterprise, vous ne pouvez pas réellement
utiliser la liaison anticipée mais, comme le module de données distant utilise une
interface double, vous pouvez utiliser la dispinterface du serveur d’applications
pour améliorer les performances d’une simple liaison tardive. La dispinterface
porte le même nom que l’interface du module de données distant, suivi de la
chaîne ’Disp’. Vous pouvez affecter à la propriété AppServer une variable de ce
type pour obtenir la dispinterface. Par exemple :
var
TempInterface: IMyAppServerDisp;
begin
TempInterface := MyConnection.AppServer;
...
TempInterface.SpecialMethod(x,y);
...
end;
Remarque Pour utiliser la dispinterface, vous devez ajouter l’unité _TLB qui est générée
lorsque vous enregistrez la bibliothèque de types à la clause uses de votre
module client.

14-28 Guide du développeur


Gestion des transactions dans les applications multiniveaux

Gestion des transactions dans les applications multiniveaux


Lorsque les applications client appliquent les mises à jour sur le serveur
d’applications, le composant fournisseur enveloppe automatiquement le
processus d’application des mises à jour et de résolution des erreurs dans une
transaction. Cette transaction est validée si le nombre d’enregistrements
problématiques n’est pas supérieur à la valeur MaxErrors spécifiée comme
argument à la méthode ApplyUpdates. Sinon, elle est annulée.
De plus, vous pouvez améliorer la prise en charge des transactions sur votre
application serveur en ajoutant un composant base de données ou en utilisant
SQL direct. La gestion des transactions est la même que dans une application à
niveau double. Pour plus d’informations sur ce type de gestion de transactions,
voir “Utilisation des transactions” à la page 13-5 et “Utilisation des transactions
de connexion” à la page 23-11.
Si vous utilisez MTS, vous pouvez améliorer la prise en charge des transactions
à l’aide des transactions MTS. Les transactions MTS peuvent inclure toute
logique d’entreprise sur votre serveur d’applications et ne se limitent pas à la
gestion de l’accès aux bases de données. De plus, comme elles gèrent la
validation en deux phases, les transactions MTS peuvent englober plusieurs
bases de données.
Attention La validation en deux phases n’est entièrement implémentée que sur les bases de
données Oracle7 et MS-SQL. Si votre transaction implique plusieurs bases de
données et que certaines d’entre elles sont des serveurs distants autres que
Oracle7 ou MS-SQL, il existe un risque minime d’échec partiel. Toutefois, dans
toute base de données, vous pouvez utiliser les transactions.
Pour utiliser les transactions, étendez l’interface du serveur d’applications afin
d’inclure des appels de méthode qui encapsulent la transaction, si nécessaire.
Lorsque vous configurez le module de données distant MTS, indiquez qu’il doit
participer aux transactions. Lorsqu’un client appelle une méthode sur l’interface
de votre serveur d’applications, il est automatiquement inclus dans une
transaction. Tous les appels client vers votre serveur d’applications sont
répertoriés dans cette transaction jusqu’à ce que vous indiquiez qu’elle est
achevée. Ces appels réussissent en bloc ou sont annulés.
Remarque Ne combinez pas les transactions MTS avec les transactions explicites créées par
un composant base de données ou à l’aide de SQL direct. Lorsque votre module
de données distant est répertorié dans une transaction MTS, il répertorie
automatiquement tous les appels de votre base de données dans la transaction.
Pour plus d’informations sur l’utilisation des transactions MTS, voir “Support
transactionnel MTS” à la page 50-7.

Création d’applications multiniveaux 14-29


Gestion des relations maître / détail

Gestion des relations maître / détail


Vous pouvez créer des relations maître/détail entre les ensembles de données
client de votre application client de la même façon que vous définissez des fiches
maître/détail dans les applications à niveau unique ou à niveau double. Pour
plus d’informations sur la définition de fiches maître/détail, voir “Création de
fiches maître-détail” à la page 20-27.
Toutefois, cette approche présente deux inconvénients majeurs :
• Tous les enregistrements de la table détail doivent provenir du serveur
d’applications même si elle n’utilise qu’un ensemble détail à la fois. Ce
problème peut être atténué à l’aide de paramètres. Pour plus d’informations,
voir “Limitation des enregistrements avec des paramètres” à la page 24-19.
• Il est très difficile d’appliquer les mises à jour car les ensembles de données
client les appliquent au niveau de l’ensemble de données alors que les mises à
jour maître/détail englobent plusieurs ensembles de données. Même dans un
environnement à niveau double, où vous pouvez appliquer les mises à jour
pour plusieurs tables dans une seule transaction, l’application des mises à jour
dans des fiches maître/détail est délicate. Voir “Application des mises à jour à
des tables maître / détail” à la page 25-8 pour plus d’informations sur
l’application des mises à jour dans les fiches maître/détail ordinaires.
Dans les applications multiniveaux, vous pouvez éviter ces problèmes en
utilisant des tables imbriquées pour représenter la relation maître/détail. Pour ce
faire, définissez une relation maître/détail entre les tables sur le serveur
d’applications puis initialisez la propriété DataSet de votre composant fournisseur
sur la table maître.
Lorsque les clients appellent la méthode GetRecords du fournisseur, il inclut
automatiquement les ensembles de données détail en tant que champ ensemble
de données dans les enregistrements du paquet de données. Lorsque les clients
appellent la méthode ApplyUpdates du fournisseur, il traite automatiquement
l’application des mises à jour dans l’ordre adéquat.
Voir “Représentation des relations maître / détail” à la page 24-3 pour plus
d’informations sur l’utilisation des ensembles de données imbriqués pour gérer
les relations maître/détail dans les ensembles de données client.

Gestion des informations d’état dans les modules de données


distants
L’interface IAppServer, qui contrôle toute la communication entre les ensembles
de données client et les fournisseurs sur le serveur d’applications, est
généralement sans état. Lorsqu’une application est sans état, elle ne “mémorise”
pas ce qui s’est produit lors des appels précédents effectués par le client. Cette
caractéristique est utile si vous regroupez les connexions aux bases de données
sous MTS, car votre serveur d’applications n’a pas besoin de différencier les

14-30 Guide du développeur


Gestion des informations d’état dans les modules de données distants

connexions par rapport à des informations persistantes comme l’enregistrement


en cours. De même, cette absence d’information d’état est importante lorsque
vous partagez les instances des modules de données distants entre de nombreux
clients, comme cela se produit avec l’activation “juste à temps” MTS, le
regroupement des objets, ou les serveurs CORBA classiques.
Dans certaines circonstances, toutefois, vous voudrez maintenir des informations
d’état entre les appels au serveur d’applications. Par exemple, lorsque vous
demandez des données à l’aide de la lecture incrémentale, le fournisseur sur le
serveur d’applications doit “mémoriser” des informations sur les appels
précédents (l’enregistrement en cours).
Cela ne pose pas de problème si le module de données distant est configuré de
sorte que chaque client en possède sa propre instance. Lorsque chaque client a sa
propre instance du module de données distant, aucun autre client ne peut
modifier l’état du module de données entre les appels du client.
Mais, il est légitime de vouloir bénéficier du partage des instances des modules
de données distants en même temps que de la gestion des informations d’état
persistantes. Par exemple, vous pouvez avoir besoin d’utiliser la lecture
incrémentale afin d’afficher un ensemble de données trop volumineux pour tenir
en mémoire en une seule fois.
Avant et après tous les appels à l’interface IAppServer que l’ensemble de données
client envoie au serveur d’applications (AS_ApplyUpdates, AS_Execute,
AS_GetParams, AS_GetRecords ou AS_RowRequest), il reçoit un événement dans
lequel il peut envoyer ou extraire des informations d’état personnalisées. De
même, avant et après la réponse des fournisseurs aux appels générés par les
clients, ils reçoivent des événements dans lesquels ils peuvent extraire ou
envoyer des informations d’état personnalisées. Avec ce mécanisme, vous pouvez
communiquer des informations d’état persistantes entre les applications client et
le serveur d’applications, même si le serveur d’applications est sans état. Par
exemple, pour utiliser la lecture incrémentale dans un serveur d’applications sans
état, vous pouvez faire ce qui suit :
• Utilisez l’événement de l’ensemble de données client BeforeGetRecords pour
envoyer au serveur d’applications la valeur de la clé du dernier enregistrement :
TDataModule1.ClientDataSet1BeforeGetRecords(Sender: TObject; var OwnerData: OleVariant);
var
CurRecord: TBookMark;
begin
with Sender as TClientDataSet do
begin
CurRecord := GetBookmark; { sauvegarde l’enregistrement en cours }
try
Last; {localise le dernier enregistrement dans le nouveau paquet }
OwnerData := FieldValues['Key']; { envoie la valeur clé au serveur d’applications }
GotoBookmark(CurRecord); { revient à l’enregistrement en cours }
finally
FreeBookmark(CurRecord);
end;
end;
end;

Création d’applications multiniveaux 14-31


Ecriture des applications MIDAS Web

• Sur le serveur, utilisez l’événement du fournisseur BeforeGetRecords pour


localiser l’ensemble d’enregistrements approprié :
TRemoteDataModule1.Provider1BeforeGetRecords(Sender: TObject; var OwnerData: OleVariant);
begin
with Sender as TProvider do
DataSet.Locate('Key', OwnerData, []);
end;
Remarque L’exemple précédent utilise une valeur clé pour marquer la fin de l’ensemble
d’enregistrements plutôt qu’un signet (bookmark). C’est que les signets risquent
de ne pas être valides d’un appel à IAppServer à l’autre si l’application serveur
regroupe les handles de bases de données.

Ecriture des applications MIDAS Web


Si vous voulez créer des clients basés sur le Web pour votre application de bases
de données multiniveau, vous devez remplacer le niveau client par une
application Web spéciale qui agit simultanément en tant que client pour le
serveur d’applications et en tant qu’application serveur Web installée sur la
même machine. que le serveur Web. Cette architecture est illustrée par la
figure 14.1.
Figure 14.1 Application de bases de données multiniveau basée sur le Web

B a se d e
d on né es
d istan te
N a viga te ur

Il existe deux façons d’envisager la construction de l’application MIDAS Web :


• Vous pouvez combiner l’architecture MIDAS au support d’ActiveX fourni par
pour distribuer une application client en tant que contrôle ActiveX. Cela
permet à n’importe quel navigateur supportant ActiveX d’exécuter votre
application client en tant que serveur en processus.
• Vous pouvez utiliser les paquets de données XML pour construire une
application InternetExpress. Cela permet aux navigateurs supportant javascript
d’interagir avec votre application client par le biais de pages html.
Ces deux approches sont très différentes. Votre choix dépendra des
considérations suivants :
• Chaque approche repose sur une technologie différente (ActiveX contre
javascript et XML). Prenez en compte les systèmes de vos utilisateurs finals.
La première approche exige un navigateur supportant ActiveX (ce qui limite
vos clients aux plates-formes Windows). La seconde exige un navigateur
supportant javascript et les capacités DHTML introduites par Netscape 4 et
Internet Explorer 4.

14-32 Guide du développeur


Ecriture des applications MIDAS Web

• Les contrôles ActiveX doivent être téléchargés sur le navigateur pour agir en
tant que serveur en processus. Il s’en suit que les clients utilisant l’approche
ActiveX exigent beaucoup plus de mémoire que les clients d’une application
basée sur html.
• L’approche InternetExpress peut s’intégrer à d’autres pages HTML. Un client
ActiveX doit être exécuté dans une fenêtre à part.
• L’approche InternetExpress utilise HTTP standard, excluant donc les
problèmes de coupe-feu auxquels sont confrontées les applications ActiveX.
• L’approche ActiveX autorise une plus grande souplesse dans la façon de
programmer votre application. Vous n’êtes pas limité par les capacités des
bibliothèques javascript. Les ensembles de données client utilisés dans
l’approche ActiveX exposent plus de fonctionnalités (filtres, fourchettes,
globalisation, paramètres facultatifs, lecture différée des BLOB ou des détails
imbriquées, etc.) que les navigateurs XML utilisés par l’approche
InternetExpress.
Attention Votre application client Web risque d’apparaître et de se comporter de manière
différente selon le navigateur qui l’affiche. Testez-la avec les navigateurs dont se
serviront vos utilisateurs finals.

Distribution d’une application client en tant que contrôle ActiveX


L’architecture MIDAS peut être combinée avec les fonctionnalités ActiveX de
Delphi pour distribuer une application client en tant que contrôle ActiveX.
Lorsque vous distribuez votre application client en tant que contrôle ActiveX,
créez le serveur d’applications comme dans toute autre application multiniveau.
La seule contrainte est que vous serez amené à utiliser DCOM, HTTP ou sockets
comme protocole de communication, car le logiciel runtime OLEnterprise ou
CORBA n’est pas installé sur les machines client. Pour plus de détails sur la
création du serveur d’applications, voir “Création du serveur d’applications” à la
page 14-13.
Lorsque vous créez l’application client, vous devez utiliser une fiche active
comme base au lieu d’une fiche ordinaire. Voir “Création d’une fiche active pour
l’application client”, ci-dessous, pour plus de détails.
Une fois que vous avez construit et déployé votre application client, celle-ci est
accessible depuis n’importe quelle machine munie d’un navigateur Web ActiveX.
Pour qu’un navigateur Web lance votre application client, le serveur Web doit
être exécuté sur la machine qui héberge l’application client.
Si l’application client utilise DCOM pour communiquer avec le serveur
d’applications, la machine qui héberge le navigateur Web doit être activée pour
fonctionner avec DCOM. Si la machine hébergeant le navigateur Web est une
machine Windows 95, DCOM95, disponible auprès de Microsoft, doit y être
installé.

Création d’applications multiniveaux 14-33


Ecriture des applications MIDAS Web

Création d’une fiche active pour l’application client


1 Comme l’application client doit être déployée en tant que contrôle ActiveX, un
serveur Web doit être exécuté sur le même système que l’application client.
Vous pouvez utiliser un serveur immédiatement opérationnel, tel que Personal
Web Server de Microsoft, ou écrire le vôtre à l’aide des composants socket
décrits dans le chapitre 30, “Utilisation des sockets”
2 Créez l’application client en suivant la procédure décrite dans “Création de
l’application client” à la page 14-21, à la différence toutefois que vous devez
commencer en choisissant Fichier|Nouveau|Fiche active, plutôt qu’en
démarrant le projet client comme un projet Delphi ordinaire.
3 Si votre application client utilise un module de données, ajoutez un appel
pour créer de façon explicite le module de données dans l’initialisation de la
fiche active.
4 Lorsque votre application client est achevée, compilez le projet et sélectionnez
Projet | Options de déploiement Web. Dans la boîte de dialogue Options de
déploiement Web, vous devez procéder comme suit :
1 Sur la page Projet, spécifiez le répertoire destination, l’URL du répertoire
cible et le répertoire HTML. Habituellement, le répertoire destination et le
répertoire HTML sont les mêmes que les répertoires de projets de votre
serveur Web. L’URL cible est habituellement le nom de la machine serveur
spécifiée dans les paramètres Windows Network|DNS.
2 Sur la page Fichiers supplémentaires, incluez midas.dll dans votre
application client.
5 Enfin, sélectionnez Projet|Déployer pour le Web, pour déployer l’application
client en tant que fiche active.
N’importe quel navigateur Web pouvant exécuter des fiches actives peut
exécuter votre application client. Il suffit que soit spécifié le fichier .HTM créé
lorsque vous avez déployé l’application client. Ce fichier .HTM porte le même
nom que votre projet d’application client et apparaît dans le répertoire spécifié
comme répertoire destination.

Construction des applications Web avec InternetExpress


Les clients MIDAS peuvent exiger du serveur d’applications la fourniture de
données codées en XML à la place d’OleVariants. En combinant les paquets de
données codées XML, bibliothèques javascript spéciales de fonctions pour bases
de données, et le support des applications serveur Web de Delphi, vous pouvez
créer des applications client simples accessibles via un navigateur Web
supportant javascript. Ces applications constituent le support InternetExpress de
Delphi.
Avant d’entreprendre la construction d’une application InternetExpress , vous
devez comprendre l’architecture des applications serveur Web de Delphi . Elle
est décrite dans le chapitre 29, “Création d’applications serveur pour Internet”.

14-34 Guide du développeur


Ecriture des applications MIDAS Web

La page InternetExpress de la palette de composants contient un jeu de


composants étendant l’architecture d’application serveur Web pour agir en tant
que client MIDAS. Avec ces composants, l’application Web génère des pages
HTML qui associent HTML, XML et javascript. HTML régit la disposition et
l’aspect des pages vues par les utilisateurs finals dans leur navigateur. XML code
les paquets de données et les paquets delta qui représentent les informations
base de données. Javascript permet aux contrôles HTML d’interpréter et de
manipuler les données des paquets XML.
Si l’application InternetExpress utilise DCOM pour se connecter au serveur
d’applications, vous devez suivre des étapes supplémentaires afin de garantir
que le serveur d’applications accorde les droits d’accès et de lancement à ses
clients. Voir “Droits d’accès au serveur d’applications et à son lancement” à la
page 14-37 for détails.
Astuce Vous pouvez utiliser les composants de la page InternetExpress pour construire
des applications serveur Web avec des données “réelles” même si vous n’avez
pas d’application serveur. Ajoutez simplement un fournisseur et son ensemble de
données au module Web.

Construction d’une application InternetExpress


Les étapes suivantes décrivent comment construire une application Web créant
des pages HTML pour permettre aux utilisateurs d’interagir sur les données
depuis un serveur d’applications via un navigateur Web javascript.
1 Choisissez Fichier|Nouveau pour afficher la boîte de dialogue Nouveaux
éléments. Dans la page Nouveau, sélectionnez Application serveur Web. Cette
procédure est décrite par “Création d’applications serveur Web” à la
page 29-6.
2 Depuis la page MIDAS de la palette de composants, ajoutez un composant
connexion au module Web qui apparaît lorsque vous créez une nouvelle
application serveur Web. Le type du composant connexion dépendra du
protocole de communication utilisé. Voir “Sélection d’un protocole de
connexion” à la page 14-9 pour plus de détails.
3 Définissez les propriétés de votre composant connexion pour spécifier le
serveur d’applications avec lequel il doit établir la connexion. Pour en savoir
plus sur la définition d’un composant connexion, voir “Connexion au serveur
d’applications” à la page 14-22.
4 Depuis la page InternetExpress de la palette de composants, ajoutez au
module Web un XML courtier au lieu d’un ensemble de données client.
Comme TClientDataSet, TXMLBroker représente les données venant d’un
fournisseur sur le serveur d’applications et interagit avec le serveur
d’applications via son interface IAppServer. Cependant, au contraire des
ensembles de données, les courtiers XML demandent des paquets de données
XML et non OleVariants, et interagissent avec les composants InternetExpress
et non les contrôles de données.

Création d’applications multiniveaux 14-35


Ecriture des applications MIDAS Web

5 Définissez la propriété RemoteServer du courtier XML pour qu’elle pointe sur


le composant connexion ajouté à l’étape 2. Définissez la propriété
ProviderName pour qu’elle indique le fournisseur sur le serveur d’applications
qui fournit les données et applique les mises à jour. Pour plus d’informations
sur la configuration d’un courtier XML, voir “Utilisation d’un courtier XML” à
la page 14-38.
6 Ajoutez au module Web un producteur de page MIDAS pour chaque page qui
sera vue par les utilisateurs dans leur navigateur. Pour chaque producteur de
page MIDAS, vous devez définir une propriété IncludePathURL. Elle indique
où se trouvent les bibliothèques javascript . Ces bibliothèques ajoutent aux
contrôles HTML des capacités de gestion des données.
7 Cliquez avec le bouton droit sur une page Web et choisissez Editeur d’actions
pour afficher l’éditeur d’actions. Ajoutez l’élément d’action correspondant à
chaque message que vous voulez gérer depuis les navigateurs. Associez les
producteurs de page que vous avez ajouté à l’étape 6 avec ces actions en
définissant leur propriété Producer ou en écrivant du code dans un
gestionnaire de l’événement OnAction. Pour plus d’informations sur l’ajout des
éléments d’action dans l’éditeur d’actions, voir “Ajout d’actions au répartiteur”
à la page 29-9.
8 Double-cliquez sur chaque page Web pour afficher l’éditeur de pages Web
(vous pouvez également afficher cet éditeur en cliquant dans l’inspecteur
d’objets sur le bouton points de suspension situé à côté de la propriété
WebPageItems). Dans l’éditeur, vous pouvez ajouter des éléménts Web pour
concevoir les pages que verront les utilisateurs dans leur navigateur. Pour
plus d’informations sur la conception de pages Web pour votre application
InternetExpress, voir “Création des pages Web avec un producteur de page
MIDAS” à la page 14-40.
9 Construisez votre application Web. Une fois l’application installée avec votre
serveur Web, les navigateurs pourront l’appeler en spécifiant le nom de
l’application dans la portion de l’URL correspondant au nom de script et le
nom du composant page Web dans la partie correspondant au chemin d’accès.

Utilisation des bibliothèques javascript


Les pages HTML générées par les composants InternetExpress et les éléments
Web qu’elles contiennent utilisent plusieurs bibliothèques javascript livrées avec
Delphi:

Table 14.4 Les bibliothèques javascript


Bibliothèque fonctions
xmldom.js Cette bibliothèque est un analyseur XML compatible DOM écrit en
javascript. Il permet aux analyseurs ne supportant pas XML d’utiliser les
paquets de données XML.
xmldb.js Cette bibliothèque définit des classes d’accès aux données analogues à
TClientDataSet et TField.
xmldisp.js Cette bibliothèque définit des classes associant les classes d’accès aux
données de xmldb aux contrôles HTML de la page HTML.

14-36 Guide du développeur


Ecriture des applications MIDAS Web

Ces bibliothèques sont dans le répertoire Source/Webmidas. Lorsque vous les


aurez installées, vous devrez définir la propriété IncludePathURL de tous les
producteurs de page MIDAS pour indiquer où elles se trouvent.
Il est possible d’écrire vos propres pages HTML en utilisant les classes javascript
fournies dans ces bibliothèques au lieu d’utiliser les éléments Web pour générer
vos pages Web. Toutefois, vous devez vérifier que votre code ne fait rien
d’illégal car ces classes comportent une vérification d’erreurs minimale (afin de
réduire la taille des pages Web générées).
Les classes des bibliothèques javascript sont un standard en cours de
développement, elles sont régulièrement mises à jour. Si vous voulez les utiliser
directement plutôt que de laisser les éléments Web générer le code javascript,
vous pouvez obtenir leurs dernières versions et une documentation sur leur
utilisation à l’adresse www.borland.com/CodeCentral.

Droits d’accès au serveur d’applications et à son lancement


Les demandes issues de l’application InternetExpress apparaissent au serveur
d’applications comme provenant d’un compte invité dont le nom est
IUSR_computername, où computername est le nom du système qui exécute
l’application Web. Par défaut, ce compte n’a pas le droit d’accéder au serveur
d’applications ni de le lancer. Si vous essayez d’utiliser l’application Web sans
accorder ces droits, lorsque le navigateur Web tente de charger la page
demandée, un dépassement de délai se produit avec l’erreur EOLE_ACCES_ERROR.
Remarque Le serveur d’applications s’exécutant sous le compte invité, il ne peut être arrêté
par aucun autre compte.
Pour accorder à l’application Web le droit d’accéder au serveur d’applications et
de le lancer, exécutez DCOMCnfg.exe, qui se trouve dans le répertoire System32
de la machine exécutant le serveur d’applications. Les étapes suivantes décrivent
comment configurer votre serveur d’applications :
1 Lorsque vous exécutez DCOMCnfg, sélectionnez votre serveur dans la liste
des applications de la page Applications.
2 Cliquez sur le bouton Propriétés. Lorsque le dialogue change, sélectionnez la
page Sécurité.
3 Sélectionnez Permissions d'accès personnalisées, et appuyez sur le bouton
Modifier. Ajoutez le nom IUSR_computername à la liste de comptes ayant un
droit d’accès, où computername est le nom de la machine exécutant
l’application Web.
4 Sélectionnez Permissions de lancement personnalisées, et appuyez sur le
bouton Modifier. Ajoutez IUSR_computername à la liste.
5 Cliquez sur le bouton Appliquer.

Création d’applications multiniveaux 14-37


Ecriture des applications MIDAS Web

Utilisation d’un courtier XML


Un courtier XML a deux fonctions principales :
• Il lit à partir du serveur d’applications les paquets de données XML et les
rend disponibles aux éléments Web générant le HTML pour l’application
InternetExpress.
• Il reçoit des navigateurs les mises à jour au format des paquets delta XML et
les applique au serveur d’applications.

Lecture des paquets de données XML


Avant que le courtier XML fournisse les paquets de données XML aux
composants générant les pages HTML, il doit les lire depuis le serveur
d’applications. Pour cela, il utilise l’interface IAppServer du serveur
d’applications, qu’il acquiert via un composant connexion. Vous devez définir les
propriétés suivantes pour que le producteur XML utilise l’interface IAppServer du
serveur d’applications :
• Définissez la propriété RemoteServer par le composant connexion qui établit la
connexion au serveur d’applications et obtient son interface IAppServer. Au
moment de la conception, vous pouvez sélectionner cette valeur dans
l’inspecteur d’objets à partir d’une liste déroulante.
• Définissez la propriété ProviderName par le nom du composant fournisseur sur
le serveur d’applications qui représente l’ensemble de données pour lequel
vous voulez les paquets XML. Ce fournisseur fournit les paquets de données
XML et applique les mises à jour à partir des paquets delta XML. Au moment
de la conception, si la propriété RemoteServer est définie et si le composant
connexion a une connexion active, l’inspecteur d’objets affiche la liste des
fournisseurs disponibles. (Si vous utilisez une connexion DCOM, le serveur
d’applications doit être recensé sur la machine client).
Deux propriétés vous permettent d’indiquer les informations à inclure dans les
paquets de données.
• Si le fournisseur sur le serveur d’applications représente une requête ou une
procédure stockée, vous pouvez souhaiter transmettre les valeurs des
paramètres avant d’obtenir le paquet de données XML. Vous pouvez fournir
ces valeurs à l’aide de la propriété Params.
• Vous pouvez limiter le nombre d’enregistrements ajoutés au paquet de
données en définissant la propriété MaxRecords.
Les composants qui génèrent le code HTML et javascript pour l’application
InternetExpress utilisent automatiquement le paquet de données XML du courtier
XML lorsque vous avez défini leur propriété XMLBroker. Pour obtenir le paquet
de données XML directement dans le code, utilisez la méthode RequestRecords.

14-38 Guide du développeur


Ecriture des applications MIDAS Web

Remarque Quand le courtier XML fournit un paquet de données à un autre composant (ou
que vous appelez RequestRecords), il reçoit un événement OnRequestRecords. Vous
pouvez utiliser cet événement pour fournir votre propre chaîne XML au lieu du
paquet de données du serveur d’applications. Par exemple, vous pouvez lire le
paquet de données XML à partir du serveur d’applications à l’aide de
GetXMLRecords, puis le modifier avant de le fournir à la page Web.

Application des mises à jour à partir des paquets delta XML


Lorsque vous ajoutez le courtier XML au module Web (ou à un module de
données contenant un TWebDispatcher), il se recense lui-même automatiquement
avec le répartiteur Web en tant qu’objet auto-réparti. Cela veut dire qu’au
contraire des autres composants, il n’est pas nécessaire de créer un élément
d’action pour le courtier XML afin qu’il réponde aux messages de mise à jour
provenant du navigateur Web. Ces messages contiennent les paquets delta XML
à appliquer au serveur d’applications. Habituellement, ils proviennent d’un
bouton que vous avez créé dans une des pages HTML produites par
l’application client Web.
Afin que le répartiteur reconnaisse les messages pour le courtier XML, vous
devez les décrire à l’aide de la propriété WebDispatch. Définissez la propriété
PathInfo par la partie chemin d’accès de l’URL à laquelle sont envoyés les
messages du courtier XML. Définissez MethodType par la valeur d’entête de la
méthode des messages de mise à jour adressés à cette URL (classiquement
mtPost). Si vous souhaitez répondre à tous les messages avec le chemin d’accès
spécifié, définissez MethodType par mtAny. Si vous ne voulez pas que le courtier
XML réponde directement aux messages de mise à jour (par exemple, si vous
souhaitez les gérer explicitement en utilisant un élément d’action), définissez la
propriété Enabled par False. Pour plus d’informations sur la façon dont le
répartiteur Web détermine les composants gérant les messages du navigateur
Web, voir “Répartition des messages de requête” à la page 29-9.
Lorsque le répartiteur passe un message de mise à jour au courtier XML, il passe les
mises à jour sur le serveur d’applications et il reçoit éventuellement un paquet delta
XML décrivant toutes les erreurs de mise à jour qui se sont produites. Finalement, il
envoie un message de réponse au navigateur, qui redirige le navigateur sur la page
ayant généré le paquet delta XML delta ou lui envoie un nouveau contenu.
Certains événements vous permettent d’intégrer des traitements personnalisés au
niveau de chacune des étapes de ce processus de mise à jour :
1 La première fois que le répartiteur passe le message de mise à jour au courtier
XML, il reçoit un événement BeforeDispatch, où vous pouvez pré-traiter la
demande ou même la gérer entièrement. Cet événement permet au courtier
XML de gérer d’autres messages que les messages de mise à jour.
2 Si le gestionnaire de l’événement BeforeDispatch ne gère pas le message, le
courtier XML reçoit un événement OnRequestUpdate, où vous pouvez
appliquer les mises à jour vous-même plutôt que de suivre le processus par
défaut.

Création d’applications multiniveaux 14-39


Ecriture des applications MIDAS Web

3 Si le gestionnaire de l’événement OnRequestUpdate ne gère pas la demande, le


courtier XML applique les mises à jour et reçoit un paquet delta contenant les
erreurs s’y rapportant.
4 S’il n’y a pas d’erreur de mise à jour, le courtier XML reçoit un événement
OnGetResponse, où vous pouvez créer un message de réponse indiquant que
les mises à jour ont été appliquées avec succès ou envoyer des données
rafraîchies au navigateur. Si le gestionnaire de l’événement OnGetResponse
n’achève pas la réponse (ne définit pas le paramètre Handled par True), le
courtier XML envoie une réponse qui redirige le navigateur sur le document
ayant généré le paquet delta.
5 S’il y a des erreurs de mise à jour, le courtier XML reçoit un événement
OnGetErrorResponse. Vous pouvez utiliser cet événement pour tenter de
résoudre les erreurs ou pour générer une page Web qui les décrit à
l’utilisateur final. Si le gestionnaire de l’événement OnGetErrorResponse
n’achève pas la réponse (ne définit pas le paramètre Handled par True), le
courtier XML appelle un générateur de contenu particulier, appelé le
ReconcileProducer, pour générer le contenu du message de réponse.
6 Enfin, le courtier XML reçoit un événement AfterDispatch, où vous pouvez
effectuer toutes les actions voulues avant de renvoyer une réponse au
navigateur Web.

Création des pages Web avec un producteur de page MIDAS


Chaque producteur de page MIDAS génère un document HTML qui apparaît
dans les navigateurs des clients de votre application. Si votre application
comprend plusieurs documents Web séparés, utilisez un producteur de page
MIDAS différent pour chacun d’entre eux.
Le producteur de page MIDAS est un composant producteur de pagespécial.
Comme les autres producteurs de page, vous pouvez l’assigner à la propriété
Producer d’un élément d’action ou l’appeler explicitement à partir du gestionnaire
de l’événement OnAction. Pour plus d’informations sur l’utilisation des
producteurs de contenu avec les éléments d’action, voir “Réponse aux messages
de requête avec des éléments d’action” à la page 29-12. Pour plus d’informations
sur les producteurs de page, voir “Utilisation du composant générateur de page”
à la page 29-19.
Contrairement à la majorité des producteurs de page, le producteur de page
MIDAS dispose d’un modèle par défaut, valeur de sa propriété HTMLDoc. Ce
modèle contient un jeu de balises transparentes pour HTML que le producteur
de page MIDAS utilise pour assembler le document HTML (avec javascript et
XML imbriqués) en incluant le contenu produit par les autres composants. Avant
qu’il soit possible de traduire toutes les balises transparentes pour HTML et
d’assembler le document, vous devez indiquer l’emplacement des bibliothèques
javascript utilisées par le code javascript imbriqué dans la page. Cet
emplacement est spécifié en définissant la propriété IncludePathURL.

14-40 Guide du développeur


Ecriture des applications MIDAS Web

Vous pouvez spécifier les composants générant chaque partie de la page Web
avec l’éditeur de pages Web. Affichez l’éditeur de pages Web en double-cliquant
sur le composant page Web ou, dans l’inspecteur d’objets, en cliquant sur le
bouton points de suspension situé à côté de la propriété WebPageItems.
Les composants que vous ajoutez dans l’éditeur de pages Web génèrent le code
HTML qui remplace une des balises transparentes pour HTML du modèle par
défaut du producteur de page MIDAS. Ces composants constituent la valeur de
la propriété WebPageItems. Après avoir inséré les composants dans l’ordre qui
vous convient, vous pouvez personnaliser le modèle pour ajouter votre propre
code HTML ou modifier les balises par défaut.

Utilisation de l’éditeur de pages Web


L’éditeur de pages Web vous permet d’ajouter des éléments Web à votre
producteur de page MIDAS et de voir la page HTML qui en résulte. Affichez
l’éditeur de pages Web en double-cliquant sur un composant producteur de page
MIDAS.
Remarque Vous devez avoir installé Internet Explorer 4, ou une version supérieure, pour
utiliser l’éditeur de pages Web.
Le haut de l’éditeur de pages Web affiche les éléments Web qui génèrent le
document HTML. Ces éléments Web sont imbriqués : chaque type d’élément
assemble le HTML généré par ses sous-éléments. Différents types d’éléments
peuvent contenir différents sous-éléments. A gauche, une arborescence affiche
tous les éléments Web en indiquant la façon dont ils s’imbriquent. A droite, vous
pouvez voir les éléments Web inclus dans l’élément en cours de sélection.
Lorsque vous sélectionnez un composant en haut de l’éditeur de pages Web,
vous pouvez définir ses propriétés dans l’inspecteur d’objets.
Cliquez sur le bouton Nouveau pour ajouter un sous-élément à l’élément en
cours de sélection. Le dialogue Ajout de composant Web montre uniquement les
éléments pouvant être ajoutés à l’élément sélectionné.
Le producteur de page MIDAS peut contenir un des deux types d’élément
suivants, chacun d’entre eux générant une fiche HTML :
• TDataForm génère une fiche HTML pour afficher les données et les contrôles
qui manipulent les données ou soumettent les mises à jour.
Les éléments que vous ajoutez à TDataForm affichent les données dans une
grille multi-enregistrement (TDataGrid) ou dans un jeu de contrôles, chacun
représentant un seul champ et un seul enregistrement (TFieldGroup). En outre,
vous pouvez ajouter un jeu de boutons pour naviguer dans les données ou
pour poster les mises à jour (TDataNavigator). Vous pouvez également ajouter
un bouton qui applique les mises à jour en retour sur le client Web
(TApplyUpdatesButton). Chacun de ces éléments contient des sous-éléments qui
représentent une valeur ou un bouton individuel. Enfin, avec la plupart des
éléments Web, vous pouvez ajouter une grille de disposition (TLayoutGroup)
permettant de personnaliser la disposition des éléments qu’ils contiennent.

Création d’applications multiniveaux 14-41


Ecriture des applications MIDAS Web

• TQueryForm génère une fiche HTML pour afficher ou lire les valeurs définies
par l’application. Par exemple, vous pouvez utiliser cette fiche pour afficher et
soumettre des valeurs de paramètres.
Les éléments que vous ajoutez à TQueryForm affichent les valeurs définies par
l’application (TQueryFieldGroup). Ils peuvent également constituer un jeu de
boutons soumettant ou réinitialisant ces valeurs (TQueryButtons). Chacun de
ces éléments contient des sous-éléments qui représentent une valeur ou un
bouton individuel. Vous pouvez également ajouter une grille de disposition à
une fiche de requête comme vous le faites à une fiche de données.
La partie inférieure de l’éditeur de pages Web affiche le code HTML généré et
vous permet de voir ce qu’il donne dans un navigateur (IE4).

Définition des propriétés des éléments Web


Les éléments Web ajoutés avec l’éditeur de pages Web sont des composants
spécialisés qui génèrent le HTML. Chaque classe d’élément Web a été conçue
pour produire un contrôle spécial ou une section du document HTML final. Un
ensemble commun de propriétés influence l’aspect du document HTML final.
Lorsqu’un élément Web représente des informations provenant du paquet de
données XML (par exemple, lorsqu’il génère un ensemble de contrôles affichant
des champs ou des paramètres, ou un bouton de manipulation des données), la
propriété XMLBroker associe l’élément Web au courtier XML gérant le paquet de
données. Vous pouvez ensuite spécifier l’ensemble contenu dans un champ
ensemble de données du paquet en utilisant la propriété XMLDataSetField. Si
l’élément Web représente un champ ou une valeur de paramètre spécifique, il
possède une propriété FieldName ou une propriété ParamName.
Vous pouvez appliquer un attribut de style à tout élément Web, jouant ainsi sur
l’aspect global de tout le document HTML qu’il génère. Styles et feuilles de style
font partie du standard HTML 4. Ils permettent à un document HTML de définir
un ensemble d’attributs d’affichage à appliquer à une balise et à tout ce qui est
de sa portée. Les éléments Web les utilisent de plusieurs façons :
• La façon la plus simple d’utiliser les styles est de définir un attribut de style
sur l’élément Web directement. Pour cela, utilisez la propriété Style. La valeur
de Style n’est rien d’autre que la partie définition d’attribut d’une définition
de style HTML standard, par exemple :
color: red.
• Vous pouvez aussi définir une feuille de style, qui détermine un ensemble de
définitions. Chaque définition inclut un sélecteur de style (soit le nom d’une
balise à laquelle le style s’applique constamment, soit le nom d’un style défini
par l’utilisateur), plus la définition entre accolades de l’attribut :
H2 B {color: red}
.MyStyle {font-family: arial; font-weight: bold; font-size: 18px }
L’ensemble global des définitions est maintenu par le producteur de page
MIDAS dans sa propriété Styles. Chaque élément Web peut alors faire référence
aux styles par les noms définis par l’utilisateur via la propriété StyleRule.

14-42 Guide du développeur


Ecriture des applications MIDAS Web

• Si vous partagez une feuille de style avec d’autres applications, vous pouvez
fournir les définitions de style dans la valeur de la propriété StylesFile du
producteur de page MIDAS et non dans la propriété Styles. Les éléments Web
individuels peuvent continuer à faire référence aux styles via la propriété
StyleRule.
Autre propriété commune des éléments Web : la propriété Custom. Custom est un
jeu d’options que vous ajoutez à la balise HTML générée. HTML définit un jeu
d’options différent pour chaque type de balise. La référence de la VCL donne un
exemple des options possibles pour la propriété Custom de la plupart des
éléments Web. Pour plus d’informations sur ces options, reportez-vous à un
guide de référence HTML.

Personnalisation du modèle d’un producteur de page MIDAS


Le modèle d’un producteur de page MIDAS est un document HTML contenant
des balises imbriquées supplémentaires que votre application traduit de manière
dynamique. Au départ, le producteur de page MIDAS génère un modèle à partir
de la valeur de la propriété HTMLDoc. Ce modèle par défaut a la forme
suivante :
<HTML>
<HEAD>
</HEAD>
<BODY>
<#INCLUDES> <#STYLES> <#WARNINGS> <#FORMS> <#SCRIPT>
</BODY>
</HTML>
Les balises transparentes pour HTML du modèle par défaut sont traduites
comme suit :
<#INCLUDES> génère les instructions qui incluent les bibliothèques javascript. Ces
instructions ont la forme suivante :
<SCRIPT language=Javascript type="text/javascript" SRC="IncludePathURL/xmldom.js"> </SCRIPT>
<SCRIPT language=Javascript type="text/javascript" SRC="IncludePathURL/xmldb.js"> </SCRIPT>
<SCRIPT language=Javascript type="text/javascript" SRC="IncludePathURL/xmlbind.js"> </SCRIPT>
<#STYLES> génère les instructions qui définissent une feuille de style à partir des
définitions listées dans la propriété Styles ou StylesFile du producteur de page
MIDAS.
<#WARNINGS> ne génère rien pendant l’exécution. Au moment de la conception, elle
ajoute des messages d’avertissement concernant les problèmes détectés lors de la
génération du document HTML. Vous pouvez voir ces messages dans l’éditeur
de pages Web.
<#FORMS> génère le HTML produit par les composants que vous avez ajoutés avec
l’éditeur de pages Web. Le HTML correspondant à chaque composant apparaît
dans l’ordre de WebPageItems.
<#SCRIPT> génère le bloc des déclarations javascript utilisées dans le HTML
généré par les composants ajoutés avec l’éditeur de pages Web.

Création d’applications multiniveaux 14-43


Ecriture des applications MIDAS Web

Vous pouvez remplacer le modèle par défaut en modifiant la valeur de


HTMLDoc ou en définissant la propriété HTMLFile. Le modèle HTML
personnalisé peut comprendre une ou plusieurs des balises transparentes pour
HTML appartenant au modèle par défaut. Le producteur de page MIDAS traduit
automatiquement ces balises lorsque vous appelez la méthode Content. En outre,
le producteur de page MIDAS traduit automatiquement les trois balises
suivantes :
<#BODYELEMENTS> est remplacé par le même HTML que celui résultant des 5 balises
du modèle par défaut. Cela peut servir à générer un modèle dans un éditeur
HTML lorsque vous voulez utiliser la disposition par défaut mais ajouter des
éléments supplémentaires à l’aide de l’éditeur.
<#COMPONENT Name=WebComponentName> est remplacé par le HTML généré par le
composant nommé WebComponentName. Ce composant peut être l’un de ceux
ajoutés dans l’éditeur de pages Web, ou tout autre composant supportant
l’interface IWebContent, il a le même propriétaire que le producteur de page
MIDAS.
<#DATAPACKET XMLBroker=BrokerName> est remplacé par le paquet de données XML
obtenu à partir du courtier XML spécifié par BrokerName. Lorsque, dans l’éditeur
de pages Web, vous examinez le HTML généré par le producteur de page
MIDAS, vous voyez cette balise à la place du paquet de données lui-même.
De plus, le modèle personnalisé peut inclure toute autre balise transparente pour
HTML que vous auriez définie. Lorsque le producteur de page MIDAS rencontre
une balise appartenant à un des sept types qu’il traduit automatiquement, il
génère un événement OnHTMLTag, dans lequel vous pouvez écrire du code pour
effectuer vos propres traductions. Pour plus d’informations sur les modèles
HTML, voir “Modèles HTML” à la page 29-19.
Astuce Les composants qui apparaissent dans l’éditeur de pages Web génèrent du code
statique. C’est dire que, sauf si le serveur d’applications modifie les métadonnées
figurant dans les paquets de données, le code HTML est toujours le même quelque
soit le moment où il est généré. Pour éviter, à l’exécution, la génération dynamique
de ce code, en réponse à chaque message de demande, vous pouvez copier le HTML
généré dans l’éditeur de pages Web et l’utiliser comme modèle. L’éditeur de pages
Web affichant une balise <#DATAPACKET> au lieu de l’XML réel, utiliser ce genre
de modèle n’empêche pas votre application de lire dynamiquement les paquets de
données provenant du serveur d’applications.

14-44 Guide du développeur


Chapitre

Utilisation des composants fournisseur


Chapter 15
15
Les composants fournisseur (TDataSetProvider) définissent le mécanisme
qu’utilisent les ensembles de données client pour obtenir leur données (sauf s’ils
utilisent des fichiers linéaires). Les fournisseurs sont responsables de
l’empaquetage des données dans des paquets client envoyés aux ensembles de
données client et d’appliquer les modifications qu’envoient les ensembles de
données client. Généralement, ils se trouvent dans un serveur d’applications
comme partie d’une application multiniveau mais ils peuvent également
apparaître comme partie de la même application que l’ensemble de données
client (ou de l’agent XML). Les fournisseurs travaillent de concert avec les
composants de résolution qui gèrent la résolution des données dans la base de
données ou l’ensemble de données.
L’essentiel de l’activité d’un composant fournisseur s’effectue automatiquement.
Vous n’avez pas besoin d’écrire de code dans le fournisseur pour créer une
application serveur opérationnelle. Cependant les composants fournisseur
proposent diverses propriétés et événements qui assurent à une application un
contrôle plus direct sur les informations empaquetées pour les clients et sur la
manière qu’a l’application de répondre aux demandes des clients.
Ce chapitre décrit comment utiliser un composant fournisseur pour contrôler les
interactions avec les applications client

Spécification de la source de données


Quand vous utilisez un composant fournisseur, vous devez spécifier un
ensemble de données à utiliser pour obtenir les données que le composant
assemble en paquets de données. Pour ce faire, définissez la valeur de la
propriété DataSet du fournisseur avec le nom de l’ensemble de données à
utiliser. A la conception, sélectionnez-le parmi les ensembles de données
disponibles dans la liste déroulante de la propriété DataSet dans l’inspecteur
d’objet.

Utilisation des composants fournisseur 15-1


Comment appliquer les modifications

TDataSetProvider peut utiliser tout ensemble de données qui gère l’interface


IProviderSupport. Cette interface est introduite par TDataSet, elle est donc disponible
dans tous les ensembles de données. Cependant, les méthodes IProviderSupport
implémentées dans TDataSet sont essentiellement des squelettes qui ne font rien ou
déclenchent des exceptions. La plupart des classes ensemble de données fournies
avec Delphi (ensembles de données BDE, ensembles de données ADO, ensembles
de données client et composant Interbase Express) redéfinissent ces méthodes pour
implémenter l’interface IProviderSupport d’une manière plus utile.
Remarque Comme le fournisseur dépend d’une interface appartenant à l’ensemble de
données, il n’a pas de dépendances spécifiques reposant sur le mécanisme
d’accès aux données (BDE, DBOLE, ou tout autre mécanisme). Ces dépendances
sont liées à l’ensemble de données que le fournisseur utilise.
Les concepteurs de composants qui créent leur propre descendant personnalisé
de TDataSet doivent redéfinir les méthodes appropriées de IProviderSupport si
leurs ensembles de données doivent fonctionner dans un serveur d’applications.
Si le fournisseur ne fournit des paquets de données qu’en mode lecture seule,
(c’est-à-dire qu’il n’applique pas les modifications), les méthodes IProviderSupport
implémentées par TDataSet peuvent suffire.

Comment appliquer les modifications


Par défaut, quand les composants TDataSetProvider appliquent les modifications
et résolvent les erreurs de modification, ils communiquent directement avec le
serveur de bases de données en utilisant des instructions SQL générées
dynamiquement. Cette approche présente cet avantage que le serveur
d’applications n’a pas besoin de fusionner les modifications deux fois (d’abord
pour l’ensemble de données, puis sur le serveur distant).
Cependant, vous pouvez préférer une autre approche : ainsi, quand vous voulez
utiliser certains des événements du composant ensemble de données. De plus,
l’ensemble de données que vous utilisez peut ne pas gérer l’utilisation d’instructions
SQL (si, par exemple, vous vous fournissez dans un composant TClientDataSet).
TDataSetProvider vous laisse décider si vous souhaitez appliquer les modifications
sur le serveur de bases de données en utilisant le SQL ou sur l’ensemble de
données source en définissant la propriété ResolveToDataSet. Quand cette
propriété a la valeur True, les modifications sont appliquées à l’ensemble de
données. Quand elle est à False, les modifications sont directement appliquées au
serveur de bases de données sous-jacent.

Contrôle des informations placées dans les paquets de données


Il y a plusieurs moyens de contrôler quelles informations sont placées dans les
paquets de données envoyés et reçus par le client. Vous pouvez :
• Spécifier les champs apparaissant dans les paquets de données.
• Spécifier des options caractérisant les paquets de données.
• Ajouter des informations personnalisées aux paquets de données.

15-2 Guide du développeur


Contrôle des informations placées dans les paquets de données

Spécification des champs apparaissant dans les paquets de


données
Pour contrôler les champs apparaissant dans les paquets de données, il faut créer
des champs persistants dans l’ensemble de données qu’utilise le fournisseur pour
construire les paquets. Le fournisseur peut alors inclure uniquement ces champs.
Les champs dont la valeur est générée dynamiquement sur le serveur (comme
les champs calculés ou les champs de référence) peuvent être utilisés, mais ils
apparaissent aux ensembles de données client comme des champs statiques en
lecture seule. Pour plus d’informations sur la création de champs persistants,
voir “Création de champs persistants” à la page 19-6.
Si l’ensemble de données client modifie les données et applique les modifications
dans le serveur d’applications, vous devez inclure suffisamment de champs pour
qu’il n’y ait pas d’enregistrements doublon dans le paquet de données. Sinon,
lors de l’application des modifications, il est impossible de déterminer les
enregistrements à actualiser. Si vous ne voulez pas que l’ensemble de données
client accède à des champs spécifiés uniquement pour assurer l’unicité, initialisez
la propriété ProviderFlags de ces champs en spécifiant pfHidden.
Remarque La nécessité de spécifier suffisamment de champs pour éviter des enregistrements
en doublon se pose également lors de l’utilisation de requêtes sur le serveur
d’applications. La requête doit contenir suffisamment de champs pour garantir
l’unicité des enregistrements même si l’application n’utilise pas tous ces champs.

Initialisation des options contrôlant les paquets de données


La propriété Options du composant fournisseur permet de spécifier si les champs
BLOB ou les tables détail imbriquées sont également envoyés, si les propriétés
d’affichage des champs sont inclues, le type de modifications autorisées, etc. Le
tableau suivant énumère les valeurs possibles pouvant être placées dans Options.

Tableau 15.1 Options d’un fournisseur


Valeur Signification
poFetchBlobsOnDemand La valeur des champs BLOB n’est pas placée dans le paquet
de données. Les applications client doivent demander ces
valeurs explicitement quand elles en ont besoin. Si la propriété
FetchOnDemand de l’ensemble de données client a la valeur
True, le client demande ces valeurs automatiquement. Sinon,
l’application client doit utiliser la méthode FetchBlobs de
l’ensemble de données client pour lire les données BLOB.
poFetchDetailsOnDemand Quand le fournisseur représente le maître dans une relation
maître-détail, les valeurs de détail imbriquées ne sont pas
placées dans le paquet de données. Les applications client
doivent demander ces valeurs quand elles sont nécessaires. Si
la propriété FetchOnDemand de l’ensemble de données client a
la valeur True, le client demande ces valeurs automatiquement.
Sinon, l’application client doit utiliser la méthode FetchDetails
de l’ensemble de données client pour lire les détails imbriqués.

Utilisation des composants fournisseur 15-3


Contrôle des informations placées dans les paquets de données

Tableau 15.1 Options d’un fournisseur (suite)


Valeur Signification
poIncFieldProps Le paquet de données contient les propriétés de champs
suivantes (quand elles sont pertinentes) : Alignment,
DisplayLabel, DisplayWidth, Visible, DisplayFormat, EditFormat,
MaxValue, MinValue, Currency, EditMask et DisplayValues.
poCascadeDeletes Quand le fournisseur représente le maître dans une relation
maître-détail, les enregistrements du détail sont supprimés
automatiquement par le serveur quand des enregistrements
maître sont supprimés. Pour utiliser cette option, le serveur de
bases de données doit être configuré pour effectuer la
suppression en cascade pour la relation d’intégrité référentielle.
poCascadeUpdates Quand le fournisseur représente le maître dans une relation
maître-détail, les valeurs de clé des tables détail sont
actualisées automatiquement quand les valeurs
correspondantes sont modifiées dans les enregistrements
maître. Pour utiliser cette option, le serveur de bases de
données doit être configuré pour effectuer les mises à jour en
cascade pour la relation d’intégrité référentielle.
poReadOnly L’ensemble de données client ne peut appliquer les
modifications au fournisseur.
poAllowMultiRecordUpdates Une seule actualisation peut entraîner la modification de
plusieurs enregistrements de la table de base de données sous-
jacente. Cela peut être fait par des déclencheurs, des
instructions SQL personnalisées, etc. Attention, s’il se produit
une erreur, les gestionnaires d’événements donnent accès à
l’enregistrement actualisé et pas aux autres enregistrements
modifiés en conséquence.
poDisableEdits Les clients ne peuvent modifier les valeurs de données
existantes. Si le client tente de modifier un champ, une
exception est déclenchée. Cela n’affecte pas la capacité du
client à insérer ou supprimer des enregistrements.
poDisableInserts Le client ne peut pas insérer de nouveaux enregistrements. Si
le client tente d’insérer un enregistrement, une exception est
déclenchée. Cela n’affecte la capacité du client à supprimer ou
modifier des enregistrements.
poDisableDeletes Le client ne peut pas supprimer d’enregistrements. Si le client
tente de supprimer un enregistrement, une exception est
déclenchée. Cela n’affecte pas la capacité du client à insérer ou
modifier des enregistrements.
poNoReset Le client ne peut pas demander au fournisseur de
repositionner le curseur sur le premier enregistrement avant
de fournir des données.
poAutoRefresh Le fournisseur rafraîchit l’ensemble de données client avec les
valeurs en cours pour les enregistrements à chaque fois qu’il
applique les modifications.
poPropogateChanges Les modifications apportées par le serveur aux enregistrements
modifiés dans le cadre du processus de mise à jour sont
renvoyées au client et fusionnées dans l’ensemble de données
client.
poAllowCommandText Le client peut redéfinir le texte de l’instruction SQL de
l’ensemble de données associé ou le nom de la table ou
procédure stockée qu’il représente.

15-4 Guide du développeur


Comment répondre aux demandes de données des clients

Ajout d’informations personnalisées aux paquets de données


Les fournisseurs peuvent placer dans les paquets de données des informations
définies par l’application en utilisant l’événement OnGetDataSetProperties. Ces
informations sont codées sous la forme d’un OleVariant et stockées sous le nom
spécifié. Les ensembles de données client de l’application client peuvent alors lire
ces informations en utilisant leur méthode GetOptionalParam. Vous pouvez
également spécifier que les informations doivent être placées dans les paquets
delta envoyés par les ensembles de données client lors de la mise à jour des
enregistrements. Dans ce cas, l’application client peut ne jamais exploiter ces
informations que le serveur s’envoie à lui-même.
Quand vous ajoutez des informations personnalisées dans l’événement
OnGetDataSetProperties, chaque attribut individuel (appelé parfois un "paramètre
optionnel") est spécifié en utilisant un tableau variant contenant trois éléments :
le nom (une chaîne), la valeur (un Variant) et un indicateur booléen indiquant si
les informations doivent être placées dans les paquets delta lorsque le client
applique les mises à jour. Vous pouvez ajouter plusieurs attributs en créant un
tableau variant de tableaux variant. Par exemple, le gestionnaire d’événement
OnGetDataSetProperties suivant envoie deux valeurs : l’heure à laquelle les
données ont été fournies et le nombre total d’enregistrements dans l’ensemble de
données source. Seule l’information indiquant l’heure à laquelle les données ont
été fournies est renvoyée quand les clients appliquent les mises à jour :
procedure TMyDataModule1.Provider1GetDataSetProperties(Sender: TObject; DataSet: TDataSet;
out Properties: OleVariant);
begin
Properties := VarArrayCreate([0,1], varVariant);
Properties[0] := VarArrayOf(['TimeProvided', Now, True]);
Properties[1] := VarArrayOf(['TableSize', DataSet.RecordCount, False]);
end;
Quand le client applique les mises à jour, l’heure à laquelle les enregistrements
ont été initialement fournis peut être lue dans l’événement OnUpdateData du
fournisseur :
procedure TMyDataModule1.Provider1UpdateData(Sender: TObject; DataSet: TClientDataSet);
var
WhenProvided: TDateTime;
begin
WhenProvided := DataSet.GetOptionalParam('TimeProvided');
...
end;

Comment répondre aux demandes de données des clients


Dans la plupart des applications multiniveaux, les demandes de données des
clients sont gérées automatiquement. Un ensemble de données client demande
un paquet de données en appelant (indirectement via l’interface IAppServer) la
méthode GetRecords. Le fournisseur répond automatiquement en lisant les
données de l’ensemble de données associé, en créant un paquet de données qui
est envoyé au client.

Utilisation des composants fournisseur 15-5


Comment répondre aux demandes de mise à jour des clients

Le fournisseur a la possibilité de modifier les données après les avoir assemblé


dans un paquet, mais avant d’envoyer le paquet au client. Le fournisseur peut,
par exemple, crypter des données confidentielles avant de les envoyer au client
ou retirer des enregistrements du paquet en se basant sur un critère (par
exemple, le rôle de l’utilisateur dans une application MTS).
Pour modifier le paquet de données avant de l’envoyer au client, écrivez un
gestionnaire de l’événement OnGetData. Le paquet de données est transmis
comme paramètre sous la forme d’un ensemble de données client. En utilisant
les méthodes de l’ensemble de données client, il est possible de modifier les
données avant de les expédier au client.
Comme tous les appels de méthode sont effectués via l’interface IAppServer, le
fournisseur a la possibilité de communiquer des informations d’état persistantes
à l’application client avant et après l’appel de GetRecords. Cette communication
s’effectue en utilisant les gestionnaires d’événements BeforeGetRecords et
AfterGetRecords. Pour davantage d’informations sur la persistance d’informations
d’état dans les serveurs d’applications, voir “Gestion des informations d’état
dans les modules de données distants” à la page 14-30.

Comment répondre aux demandes de mise à jour des clients


Un fournisseur applique les mises à jour aux enregistrements de la base de
données en se basant sur un paquet de données Delta envoyé par une
application client. Le client demande des mises à jour en appelant (indirectement,
via l’interface IAppServer) la méthode ApplyUpdates.
Comme tous les appels de méthode sont effectués via l’interface IAppServer, le
fournisseur a la possibilité de communiquer des informations d’état persistantes
à l’application client avant et après l’appel de ApplyUpdates. Cette communication
s’effectue en utilisant les gestionnaires d’événements BeforeApplyUpdates et
AfterApplyUpdates. Pour davantage d’informations sur la persistance
d’informations d’état dans les serveurs d’applications, voir “Gestion des
informations d’état dans les modules de données distants” à la page 14-30.
Quand un fournisseur reçoit un paquet de mises à jour, il génère un événement
OnUpdateData dans lequel vous pouvez modifier le paquet Delta avant qu’il ne soit
écrit dans l’ensemble de données ou déterminer comment les mises à jour sont
appliquées. Après l’événement OnUpdateData, le fournisseur utilise son composant
de résolution associé pour écrire les modifications dans la base de données.
Le composant de résolution effectue la mise à jour enregistrement par
enregistrement. Avant que la résolution ne soit appliquée à un enregistrement, il
génère un événement BeforeUpdateRecord dans le fournisseur que vous pouvez
utiliser pour filtrer avant que les modifications ne soient appliquées. Si une
erreur se produit lors de la résolution d’un enregistrement, le composant de
résolution appelle le gestionnaire d’événement OnUpdateError du fournisseur
pour résoudre l’erreur. Généralement, il se produit des erreurs car la
modification viole une contrainte du serveur ou parce que l’enregistrement a été
modifié avec une autre application après sa lecture par l’application client, mais
avant la demande du client d’appliquer la mise à jour.

15-6 Guide du développeur


Comment répondre aux demandes de mise à jour des clients

Les erreurs de mise à jour peuvent être traitées par le serveur d’applications ou
par le client. Les serveurs d’applications doivent gérer toutes les erreurs de mise
à jour qui ne nécessitent pas d’interaction avec l’utilisateur pour être résolues.
Quand le serveur d’applications ne peut résoudre une condition d’erreur, il
stocke une copie temporaire de l’enregistrement posant problème. Quand le
traitement des enregistrements est terminé, le serveur d’applications renvoie à
l’ensemble de données client le nombre d’erreurs ayant eu lieu et copie les
enregistrements non résolus dans un paquet de données résultat qu’il envoie au
client pour que celui-ci termine la réconciliation.
Les gestionnaires d’événements de tous les événements du fournisseur reçoivent
les mises à jour sous la forme d’un ensemble de données client. Si le gestionnaire
d’événement ne traite que certains types de mise à jour, vous pouvez filtrer
l’ensemble de données en vous basant sur le type de mise à jour de
l’enregistrement afin que le gestionnaire d’événement n’ait pas besoin de
parcourir des enregistrements qu’il n’utilise pas. Pour ce faire, définissez la
valeur de la propriété StatusFilter de l’ensemble de données client.
Remarque Les applications doivent proposer des fonctions supplémentaires quand les mises
à jour sont dirigées vers un ensemble de données qui représente plusieurs tables.
Pour des détails sur la manière de procéder, voir “Application des mises à jour à
des ensembles de données représentant plusieurs tables” à la page 15-10.

Modification des paquets delta avant la mise à jour de la base de


données
Avant que le fournisseur n’applique les mises à jour à la base de données, il
génère un événement OnUpdateData. Le gestionnaire d’événement OnUpdateData
reçoit en paramètre une copie du paquet Delta sous la forme d’un ensemble de
données client.
Dans le gestionnaire d’événement OnUpdateData, vous pouvez utiliser toutes les
propriétés et méthodes de l’ensemble de données client pour modifier le paquet
Delta avant de l’écrire dans l’ensemble de données. Une propriété s’avère
particulièrement utile : UpdateStatus. UpdateStatus indique le type de modification
que représente l’enregistrement en cours dans le paquet delta. Elle peut prendre
l’une des valeurs énumérées dans le tableau suivant :

Tableau 15.2 Valeurs de UpdateStatus


Valeur Description
usUnmodified Le contenu de l’enregistrement n’a pas été
modifié.
usModified Le contenu de l’enregistrement a été
modifié.
usInserted L’enregistrement a été inséré.
usDeleted L’enregistrement a été supprimé.

Utilisation des composants fournisseur 15-7


Comment répondre aux demandes de mise à jour des clients

Le gestionnaire d’événement OnUpdateData suivant insère la date en cours dans


chaque nouvel enregistrement inséré dans la base de données :
procedure TMyDataModule1.Provider1UpdateData(Sender: TObject; DataSet: TClientDataSet);
begin
with DataSet do
begin
First;
while not Eof do
begin
if UpdateStatus = usInserted then
begin
Edit;
FieldByName('DateCreated').AsDateTime := Date;
Post;
end;
Next;
end;
end;

Comment contrôler l’application des mises à jour


L’événement OnUpdateData permet également au fournisseur d’indiquer
comment les enregistrements d’un paquet delta sont appliqués à la base de
données.
Par défaut, les modifications placées dans le paquet delta sont écrites dans la
base de données en utilisant des instructions SQL UPDATE, INSERT ou DELETE
générées automatiquement, par exemple :
UPDATE EMPLOYEES
set EMPNO = 748, NAME = 'Smith', TITLE = 'Programmer 1', DEPT = 52
WHERE
EMPNO = 748 and NAME = 'Smith' and TITLE = 'Programmer 1' and DEPT = 47
Si vous ne spécifiez pas le contraire, tous les champs des enregistrements du
paquet delta sont utilisés dans les clauses UPDATE et WHERE. Vous pouvez
néanmoins exclure certains champs. Vous pouvez le faire en utilisant la propriété
UpdateMode du fournisseur. UpdateMode peut prendre l’une des valeurs
suivantes :

Tableau 15.3 Valeurs de UpdateMode


Valeur Signification
upWhereAll Tous les champs sont utilisés pour la recherche (dans la clause
WHERE).
upWhereChanged Seuls les champs clés et les champs modifiés sont utilisés pour la
recherche.
upWhereOnly Seuls les champs clé sont utilisés pour la recherche.

15-8 Guide du développeur


Comment répondre aux demandes de mise à jour des clients

Vous pouvez avoir besoin d’un contrôle encore plus précis. Par exemple, dans
l’instruction précédente, vous pouvez empêcher que le champ EMPNO ne soit
modifié en le retirant de la clause UPDATE et enlever les champs TITLE et
DEPT de la clause WHERE pour empêcher des conflits de mise à jour quand
d’autres applications ont modifié les données. Pour spécifier la clause dans
laquelle un champ spécifique apparaît, utilisez la propriété ProviderFlags.
ProviderFlags est un ensemble qui peut inclure les valeurs du tableau suivant :

Tableau 15.4 Valeurs de ProviderFlags


Valeur Description
pfInWhere Le champ n’apparaît pas dans la clause WHERE des instructions INSERT,
DELETE et UPDATE générées.
pfInUpdate Le champ n’apparaît pas dans la clause UPDATE des instructions UPDATE
générées.
pfInKey Le champ est utilisé dans la clause WHERE de l’instruction SELECT générée
qui est exécutée quand il y a échec de la mise à jour. Cette instruction
SELECT tente de trouver la valeur en cours de l’enregistrement qui a été
modifié ou supprimé, ou un enregistrement qui déclenche une violation de clé
lors de l’échec d’une insertion.
pfHidden Le champ est inclus dans les enregistrements afin de garantir l’unicité, mais il
ne doit pas être visible ou utilisé du côté client.

Par exemple, le gestionnaire d’événement OnUpdateData suivant exclut le champ


EMPNO de la clause UPDATE et les champs TITLE et DEPT de la clause WHERE :
procedure TMyDataModule1.Provider1UpdateData(Sender: TObject; DataSet: TClientDataSet);
begin
with DataSet do
begin
FieldByName('EMPNO').UpdateFlags := [ufInUpdate];
FieldByName('TITLE').UpdateFlags := [ufInWhere];
FieldByName('DEPT').UpdateFlags := [ufInWhere];
end;
end;
Remarque Vous pouvez utiliser la propriété UpdateFlags pour contrôler comment les mises à
jour sont appliquées même si vous actualisez un ensemble de données sans
utiliser d’instructions SQL générées dynamiquement. Ces indicateurs indiquent
quand même les champs utilisés pour rechercher des enregistrements et les
champs actualisés.

Filtrage des mises à jour


Avant l’application de chaque mise à jour, le fournisseur reçoit un événement
BeforeUpdateRecord. Vous pouvez utiliser cet événement pour modifier les
enregistrements avant qu’ils ne soient appliqués, tout comme vous utilisez
l’événement OnUpdateData pour modifier tout le paquet delta. Par exemple, le
fournisseur ne compare pas les champs BLOB (comme les mémos) quand il teste
les conflits de mise à jour. Si vous voulez tester les erreurs de mise à jour
concernant des champs BLOB, vous pouvez utiliser l’événement BeforeUpdateRecord.

Utilisation des composants fournisseur 15-9


Comment répondre aux demandes de mise à jour des clients

Vous pouvez également utiliser cet événement pour appliquer vous-même les
mises à jour ou pour filtrer et rejeter les mises à jour. Le gestionnaire
d’événement BeforeUpdateRecord vous permet de signaler au composant de
résolution qu’une mise à jour a déjà été gérée et qu’elle ne doit pas être
appliquée. Le mécanisme de résolution passe cet enregistrement sans le
considérer comme une erreur de mise à jour. Cet événement vous permet
d’appliquer les mises à jour dans une procédure stockée (qui ne peut être mise à
jour automatiquement), en permettant au fournisseur de ne pas effectuer le
traitement automatique une fois l’enregistrement mis à jour dans le gestionnaire
d’événement.

Résolution des erreurs de mise à jour par le fournisseur


Si une condition d’erreur apparaît alors qu’un serveur d’applications tente
d’appliquer un enregistrement du paquet delta, un événement OnUpdateError se
produit. Quand le serveur d’applications ne peut résoudre une condition
d’erreur, il stocke une copie temporaire de l’enregistrement posant problème.
Quand le traitement des enregistrements est terminé, le serveur d’applications
renvoie à l’ensemble de données client le nombre d’erreurs ayant eu lieu et copie
les enregistrements non résolus dans un paquet de données résultat qu’il envoie
au client pour que celui-ci termine la réconciliation.
Ce mécanisme vous permet de gérer toutes les erreurs de mise à jour qui ne
peuvent être résolues mécaniquement par le serveur d’applications tout en
permettant à l’utilisateur de l’application client de corriger des conditions
d’erreur.
Le gestionnaire d’événement OnUpdateError reçoit en paramètre une copie de
l’enregistrement qui n’a pu être modifié, un code d’erreur de la base de données
et une valeur indiquant si la résolution tentait d’insérer, de supprimer ou de
modifier l’enregistrement. L’enregistrement posant problème est transmis dans
un ensemble de données client. Vous ne devez jamais utiliser les méthodes de
parcours des données dans cet ensemble de données. Par contre, pour chaque
champ de cet ensemble de données vous pouvez utiliser les propriétés NewValue,
OldValue et CurValue afin de déterminer la cause du problème et effectuer les
modifications nécessaires à la résolution de l’erreur de modification. Si le
gestionnaire d’événement OnUpdateError peut corriger le problème, il doit définir
la valeur du paramètre Response afin d’appliquer l’enregistrement rectifié.

Application des mises à jour à des ensembles de données


représentant plusieurs tables
Quand un composant de résolution génère des instructions SQL qui appliquent
directement les mises à jour dans un serveur de bases de données, il lui faut le
nom de la table de la base de données qui contient les enregistrements. Cela
peut être géré automatiquement pour beaucoup d’ensembles de données, par
exemple de type TTable ou les composants TQuery "dynamiques".

15-10 Guide du développeur


Comment répondre aux événements générés par le client

Néanmoins, les mises à jour effectuées automatiquement posent problème si la


résolution doit appliquer les mises à jour aux données d’une procédure stockée
renvoyant un ensemble de résultats ou pour une requête multitable. Cela pose
problème car il n’est pas évident d’obtenir le nom de la table dans laquelle
appliquer les mises à jour.
Si la requête ou la procédure stockée est un ensemble de données BDE (TQuery
ou TStoredProc) ayant un objet de mise à jour associé, le fournisseur peut utiliser
l’objet mise à jour. Mais quand il n’y a pas d’objet mise à jour, il faut spécifier le
nom de la table par code dans un gestionnaire d’événement OnGetTableName.
Quand ce gestionnaire d’événement a indiqué un nom de table, le composant de
résolution peut générer les instructions SQL appropriées pour appliquer les
mises à jour.
Remarque La solution consistant à fournir le nom de la table ne fonctionne que si les mises
à jour doivent être appliquées à une seule table de base de données (s’il suffit de
modifier les enregistrements d’une seule table). Si la mise à jour nécessite la
modification de plusieurs tables de la base de données, vous devez explicitement
appliquer les mises à jour en utilisant l’événement BeforeUpdateRecord du
fournisseur. Quand ce gestionnaire d’événement a appliqué une mise à jour,
vous devez initialiser le paramètre Applied du gestionnaire d’événement à True
afin que le composant de résolution ne génère pas une erreur.

Comment répondre aux événements générés par le client


Les composants fournisseur implémentent un événement générique qui vous
permet de créer vos propres appels du fournisseur directement par les clients.
C’est l’événement OnDataRequest.
OnDataRequest ne rentre pas dans le fonctionnement normal d’un fournisseur.
C’est simplement un point d’entrée qui permet aux clients de communiquer
directement avec les fournisseurs sur le serveur d’applications. Ce gestionnaire
d’événement attend comme paramètre en entrée un OleVariant et renvoie un
OleVariant. En utilisant des valeurs OleVariant, vous disposez d’une interface
suffisamment générale pour vous adapter à tout type d’information reçue ou
envoyée par le fournisseur.
Pour générer un événement OnDataRequest, l’application client appelle la
méthode DataRequest de l’ensemble de données client.

Gestion des contraintes du serveur


La plupart des systèmes de gestion de bases de données relationnelles
implémentent des contraintes sur les tables afin de garantir l’intégrité des données.
Une contrainte est une règle contrôlant les valeurs des tables et des colonnes et les
relations entre les données de colonnes de différentes tables. Ainsi, la plupart des
bases de données compatibles SQL-92 gèrent les contraintes suivantes :
• NOT NULL, afin de garantir que la valeur d’une colonne est définie.

Utilisation des composants fournisseur 15-11


Gestion des contraintes du serveur

• NOT NULL UNIQUE, afin de garantir qu’une colonne contient une valeur et
que cette valeur n’existe pas dans cette colonne pour un autre enregistrement.
• CHECK, afin de garantir que la valeur d’une colonne se trouve dans un
intervalle spécifié ou qu’elle appartient à une plage limitée de valeurs autorisées.
• CONSTRAINT, une contrainte portant sur la table s’appliquant à plusieurs
colonnes.
• PRIMARY KEY, pour désigner une ou plusieurs colonnes comme clé primaire
jouant un rôle dans l’indexation.
• FOREIGN KEY, pour désigner une ou plusieurs colonnes dans une table en
référençant une autre.
Remarque Cette liste n’est pas exhaustive. Votre serveur de bases de données peut gérer
certaines ou toutes les contraintes précédentes, ainsi que d’autres. Pour
davantage d’informations sur les contraintes qu’il gère, consultez la
documentation de votre serveur.
Les contraintes d’un serveur de base de données prennent la place de
nombreuses vérifications effectuées auparavant par les applications classiques de
bases de données de bureau. Vous pouvez tirer profit des contraintes définies
par le serveur dans vos applications de bases de données multiniveaux sans
avoir à reproduire les contraintes dans le code du serveur d’applications ou dans
celui de l’application client.
Si le fournisseur utilise un ensemble de données BDE, sa propriété Constraints
vous permet de reproduire et d’appliquer les contraintes du serveur aux données
transmises ou reçues par les applications client. Si Constraints a la valeur True
(cas par défaut), les contraintes du serveur sont reproduites sur le client et
affectent les tentatives de modification des données par les clients.
Attention Avant que le serveur d’applications ne puisse transférer à l’application client les
informations relatives aux contraintes, il doit lire ces informations sur le serveur
de bases de données. Pour importer les contraintes de base de données du
serveur, utilisez l’explorateur SQL pour importer les contraintes du serveur de
base de données et les expressions par défaut dans le dictionnaire de données.
Les contraintes et les expressions par défaut du dictionnaire de données sont
automatiquement proposées par le serveur d’applications aux ensembles de
données client.
Il est parfois nécessaire de ne pas appliquer les contraintes du serveur aux
données envoyées à une application client. Ainsi quand une application client
reçoit les données en paquets et autorise la modification locale des données
avant la lecture de tous les enregistrements, il peut être nécessaire de désactiver
certaines des contraintes du serveur qui risquent d’être déclenchées à cause d’un
ensemble de données temporairement incomplet. Pour empêcher la réplication
des contraintes du serveur par l’ensemble de données client, affectez la valeur
False à Constraints. De toute façon, les ensembles de données client peuvent
désactiver ou activer les contraintes en utilisant les méthodes DisableConstraints et
EnableConstraints. Pour davantage d’informations sur l’activation et la
désactivation des contraintes par les ensembles de données client, voir “Gestion
des contraintes” à la page 24-22.

15-12 Guide du développeur


Chapitre

Gestion de sessions de bases


Chapter 16
16
de données
Les serveurs d’applications de bases de données et les applications de bases de
données client ou autonomes peuvent communiquer avec les bases de données
par l’intermédiaire du moteur de bases de données Borland (Borland Database
Engine ou BDE). Les connexions de base de données, les pilotes, les curseurs et
les requêtes d’une application sont gérés dans le contexte d’une ou de plusieurs
sessions BDE. Les sessions isolent un ensemble d’opérations d’accès aux bases de
données, comme les connexions, sans qu’il soit nécessaire de démarrer une autre
instance de l’application.
Dans une application, vous pouvez gérer des sessions BDE en utilisant les
composants TSession et TSessionList. Chaque composant TSession d’une
application encapsule une session BDE unique. Toutes les sessions intervenant
dans une application sont gérées par un seul composant TSessionList.
Toutes les applications de bases de données comportent automatiquement un
composant session appelé Session qui encapsule la session BDE par défaut. Les
applications peuvent déclarer, créer et manipuler des composants session
supplémentaires au fur et à mesure des besoins.
Les applications de bases de données comportent aussi un composant liste de
sessions appelé Sessions qui permet à l’application de gérer l’ensemble de ses
composants session.
Ce chapitre décrit les composants session et liste de sessions et explique
comment les utiliser pour contrôler des sessions BDE dans des applications de
bases de données client et avec des serveurs d’applications de bases de données.

Gestion de sessions de bases de données 16-1


Manipulation d’un composant session

Remarque Les composants session et liste de sessions comportent de nombreuses


caractéristiques implicites pouvant être utilisées telles quelles dans la plupart des
applications. Seules les applications utilisant des sessions multiples sont
susceptibles de manipuler leurs propres composants session et liste de session
(dans le cas, par exemple, où des requêtes simultanées doivent être exécutées sur
une seule base de données).

Manipulation d’un composant session


Le composant session fournit une gestion globale de l’ensemble des connexions
de base de données intervenant dans une application. Lorsque vous créez un
serveur d’applications ou une application de bases de données client, votre
application contient automatiquement un composant session appelé Session. Au
fur et à mesure qu’ils sont ajoutés à l’application, les composants base de
données et ensemble de données sont automatiquement associés à la session par
défaut. Cette session contrôle aussi les fichiers Paradox protégés par mot de
passe et spécifie les emplacements de répertoires pour la gestion des fichiers
Paradox en réseau. Les applications peuvent contrôler les connexions de bases de
données et l’accès aux fichiers Paradox en utilisant les propriétés, les événements
et les méthodes de la session.
Vous pouvez utiliser la session par défaut pour contrôler toutes les connexions
de base de données d’une application. Vous pouvez aussi ajouter des
composants session supplémentaires pendant la phase de conception, ou bien
créer dynamiquement les composants à l’exécution.
Certaines applications comme celles exécutant des requêtes simultanées sur une
même base de données requièrent des composants session supplémentaires. Dans
ce cas, chaque requête doit être exécutée dans le cadre de sa propre session. Les
applications de bases de données multithreads requièrent également plusieurs
sessions. Ces applications doivent gérer les sessions par l’intermédiaire du
composant liste de sessions, Sessions. Pour plus d’informations sur la gestion de
plusieurs sessions, voir “Gestion de plusieurs sessions” à la page 16-17.

Utilisation de la session par défaut


Toutes les applications de bases de données comportent automatiquement une
session par défaut. A chaque fois qu’une application de base de données est
exécutée, Delphi crée un composant session par défaut appelé Session (notez que
sa propriété SessionName a pour valeur “Default”). La session par défaut offre
un contrôle global sur les composants base de données qui ne sont associés à
aucune session, qu’ils soient temporaires (créés par la session à l’exécution
lorsqu’un ensemble de données qui n’est associé à aucun composant base de
données que vous avez vous-même créé est ouvert), ou persistants (explicitement
créés par votre application). La session par défaut n’est pas visible dans votre
module de données ou dans votre fiche au moment de la conception ;
néanmoins, ses propriétés et ses méthodes sont accessibles depuis votre code lors
de l’exécution.

16-2 Guide du développeur


Manipulation d’un composant session

Lorsque vous créez un composant base de données, il est automatiquement


associé à la session par défaut. L’association d’un composant base de données à
une session ayant un nom explicite n’est nécessaire que si le composant effectue
des requêtes simultanées sur une base de données qui a été précédemment
ouverte par la session par défaut. Si vous créez une application multithread, une
nouvelle session doit être créée pour gérer chaque nouveau thread.
Pour utiliser la session par défaut, il n’est pas nécessaire d’écrire de code sauf si
votre application doit effectuer l’une des opérations suivantes :
• Modifier les propriétés de la session (par exemple, spécifier le moment où les
connexions de base de données des composants base de données générés
automatiquement doivent être conservées ou abandonnées).
• Répondre à des événements de session (si votre application tente d’accéder à
un fichier Paradox protégé par mot de passe).
• Exécuter des méthodes de session (comme ouvrir ou fermer une base de
données en réponse à des actions lancées par l’utilisateur).
• Définir la propriété NetFileDir pour accéder à des tables Paradox sur réseau et
définir la propriété PrivateDir pour qu’elle pointe sur un disque dur local afin
d’augmenter les performances.
Que les composants base de données soient ajoutés à une application pendant la
phase de conception ou créés dynamiquement à l’exécution, ils sont
automatiquement associés à la session par défaut, sauf si vous leur affectez une
autre session. Si votre application tente d’ouvrir un ensemble de données n’étant
pas associé à un composant base de données, Delphi va automatiquement :
• Créer à l’exécution un composant base de données associé.
• Associer le composant base de données à la session par défaut.
• Initialiser ses propriétés clé en fonction des propriétés de la session par défaut.
Parmi ces propriétés, la propriété KeepConnections est l’une des plus importantes.
Elle détermine le moment où les connexions de bases de données sont
conservées ou abandonnées par une application. Pour plus d’informations sur la
propriété KeepConnections, voir “Spécification du comportement par défaut de
connexion aux bases de données” à la page 16-6. Les autres propriétés, méthodes
et événements du composant TSession s’appliquant à la session par défaut et aux
sessions supplémentaires sont décrits tout au long de ce chapitre.

Création de sessions supplémentaires


En plus de la session par défaut, vous pouvez créer d’autres sessions. Pendant la
phase de conception, des sessions supplémentaires peuvent être placées dans un
module de données (ou sur une fiche). Vous pouvez alors définir leurs
propriétés dans l’inspecteur d’objets, écrire des gestionnaires d’événements ou
écrire du code faisant appel à leurs méthodes. Cette section explique comment
créer et supprimer des sessions à l’exécution.

Gestion de sessions de bases de données 16-3


Manipulation d’un composant session

Remarque Gardez à l’esprit qu’il est facultatif de créer des sessions supplémentaires, sauf si
une application exécute des requêtes simultanées sur une base de données ou s’il
s’agit d’une application multithread.
Suivez les étapes ci-dessous pour activer la création dynamique d’un composant
session à l’exécution :
1 Déclarez un pointeur sur une variable TSession.
2 Instanciez une nouvelle session en appelant le constructeur Create. Le
constructeur définit une liste vide de composants base de données pour la
session, crée une liste de callbacks BDE pour la session, met la propriété
KeepConnections à True et ajoute la session à la liste des sessions gérée par le
composant liste de sessions.
3 Affectez à la propriété SessionName de la nouvelle session un nom unique.
Cette propriété est utilisée pour associer les composants base de données à la
session. Pour plus d’informations sur la propriété SessionName, voir
“Dénomination d’une session” à la page 16-4.
4 Activez la session et modifiez éventuellement ses propriétés.
Remarque Ne supprimez jamais la session par défaut.
La création et l’ouverture de sessions peuvent aussi être gérées au moyen de la
méthode OpenSession du composant TSessionList. L’utilisation de OpenSession
présente moins de risques que l’appel de Create, car OpenSession ne crée une
session que si celle-ci n’existe pas. Pour plus d’informations sur la méthode
OpenSession, voir “Gestion de plusieurs sessions” à la page 16-17.
Le code suivant crée un nouveau composant session, lui affecte un nom et ouvre
la session pour réaliser des opérations de base de données (non illustrées ici). A
la fin des opérations, il est détruit par un appel en direction de la méthode Free.
var
SecondSession: TSession;
begin
SecondSession := TSession.Create;
with SecondSession do
try
SessionName := 'SecondSession';
KeepConnections := False;
Open;
...
finally
SecondSession.Free;
end;
end;

Dénomination d’une session


La propriété SessionName sert à associer des bases de données et des ensembles
de données à une session. Pour la session par défaut, la valeur de SessionName
est “Default”. Pour chaque composant session supplémentaire que vous créez,
vous devez définir sa propriété SessionName par une valeur unique.

16-4 Guide du développeur


Manipulation d’un composant session

Les composants base de données et ensemble de données ont des propriétés


SessionName qui doivent correspondre à la propriété SessionName d’un composant
session. Si la propriété SessionName d’un composant base de données ou
ensemble de données est laissée vide, elle est automatiquement associée à la
session par défaut. Vous pouvez aussi affecter à la propriété SessionName d’un
composant base de données ou ensemble de données un nom correspondant à la
valeur SessionName d’un composant session que vous avez vous-même créé.
Pour plus d’informations sur l’utilisation du composant TSessionList (et de
Sessions en particulier) afin de contrôler plusieurs sessions à la fois, voir “Gestion
de plusieurs sessions” à la page 16-17.
Le code suivant utilise la méthode OpenSession du composant par défaut, Sessions
(de type TSessionList), pour ouvrir un nouveau composant session, puis affecte à
la propriété SessionName de ce dernier la valeur “InterBaseSession” et active la
session en lui associant le composant de base de données Database1 :
var
IBSession: TSession;
...
begin
IBSession := Sessions.OpenSession('InterBaseSession');
Database1.SessionName := 'InterBaseSession';
end;

Activation d’une session


Active est une propriété booléenne qui détermine si les composants base de
données ou ensemble de données associés à une session sont ouverts. Utilisez
cette propriété pour lire ou modifier l’état en cours des connexions de bases de
données et d’ensembles de données d’une session.
Pour déterminer l’état en cours d’une session, vérifiez la propriété Active. Si
Active vaut False (la valeur par défaut), toutes les bases de données et ensembles
de données associés à la session sont fermés. Si elle vaut True, les bases de
données et les ensembles de données sont ouverts.
Le fait de mettre Active à True déclenche l’événement OnStartup d’une session,
définit les propriétés NetFileDir et PrivateDir si elles font référence à des valeurs
assignées, et définit la propriété ConfigMode. Vous pouvez écrire un gestionnaire
d’événement OnStartup pour réaliser d’autres activités de démarrage de bases de
données. Pour plus d’informations sur OnStartup, voir “Manipulation de tables
Paradox et dBase protégées par mot de passe” à la page 16-15. Les propriétés
NetFileDir et PrivateDir sont uniquement utilisées pour établir des connexions
avec des tables Paradox. Pour plus d’informations sur ces propriétés, voir
“Spécification de l’emplacement du fichier de contrôle” à la page 16-14 et
“Spécification de l’emplacement des fichiers temporaires” à la page 16-15. La
propriété ConfigMode détermine la façon dont le BDE gère les alias BDE dans le
contexte de la session. Pour plus d’informations sur ConfigMode, voir
“Spécification de la visibilité des alias” à la page 16-11. Pour ouvrir des
composants base de données dans le cadre d’une session, voir “Création,
ouverture et fermeture des connexions de bases de données” à la page 16-7.

Gestion de sessions de bases de données 16-5


Manipulation d’un composant session

Après avoir activé une session, vous pouvez ouvrir ses connexions de base de
données en appelant la méthode OpenDatabase.
Pour les composants session que vous placez dans un module de données ou
dans une fiche, le fait de mettre Active à False quand les bases de données ou les
ensembles de données sont ouverts provoque leur fermeture. Lors de l’exécution,
cette fermeture peut provoquer l’appel d’événements associés.
Remarque Vous ne pouvez pas mettre à False la propriété Active de la session par défaut au
moment de la conception. Bien que cela soit déconseillé, il est possible de fermer
la session par défaut à l’exécution.
Au moment de la conception, utilisez l’inspecteur d’objets pour mettre Active à
False afin de désactiver tous les accès de base de données d’une session par une
simple modification de propriété. Lors de la conception d’une application, cette
opération permet d’inhiber provisoirement les exceptions provoquées par
l’indisponibilité momentanée d’une base de données distante.
Vous pouvez utiliser les méthodes Open et Close d’une session pour activer ou
désactiver des sessions autres que la session par défaut au moment de
l’exécution. Par exemple, la simple ligne de code suivante ferme toutes les bases
de données et tous les ensembles de données ouverts pour une session :
Session1.Close;
Ce code affecte la valeur False à la propriété Active de Session1. Lorsque la
propriété Active d’une session est à False, toute tentative ultérieure de la part de
l’application pour ouvrir une base ou un ensemble de données a pour effet de
réinitialiser Active à True et d’appeler le gestionnaire d’événement OnStartup de
la session, s’il est défini. Il vous est également possible de coder la réactivation
de la session à l’exécution. Le code suivant réactive Session1 :
Session1.Open;
Remarque Si une session est active, vous pouvez aussi ouvrir et fermer des connexions de
base de données particulières. Pour plus d’informations, voir “Fermeture d’une
connexion de base de données” à la page 16-8.

Personnalisation du démarrage d’une session


Il est possible de personnaliser le démarrage d’une session en écrivant un
gestionnaire d’événement OnStartup. Cet événement est déclenché quand la
session est activée. Cela se produit à sa création, puis à chaque fois que sa propriété
Active bascule de False à True (par exemple, lorsqu’une base de données ou un
ensemble de données est associé à la session alors qu’aucune base de données ou
ensemble de données n’est ouvert).

Spécification du comportement par défaut de connexion aux bases


de données
KeepConnections fournit la valeur par défaut de la propriété KeepConnection des
composants base de données temporaires créés au moment de l’exécution.

16-6 Guide du développeur


Manipulation d’un composant session

KeepConnection spécifie ce qui se produit sur une connexion établie pour un


composant base de données lorsque tous ses ensembles de données sont fermés.
Si cette propriété est à True (la valeur par défaut), une connexion constante ou
persistante est maintenue avec la base de données, même si aucun ensemble de
données n’est actif. A False, la connexion de base de données est interrompue
aussitôt que tous ses ensembles de données sont fermés.
Remarque La persistance de la connexion des composants base de données que vous placez
explicitement dans un module de données ou dans une fiche est contrôlée par
leur propriété KeepConnection. La valeur KeepConnection du composant base de
données prend le pas sur la valeur KeepConnections du composant session si les
deux valeurs diffèrent. Pour plus d’informations concernant le contrôle spécifique
des connexions de bases de données dans une session, voir “Création, ouverture
et fermeture des connexions de bases de données” à la page 16-7.
KeepConnections doit toujours être mise à True pour les applications qui ouvrent
et ferment fréquemment tous les ensembles de données associés à une base de
données située sur un serveur distant. Ce paramétrage a pour effet de réduire le
trafic sur le réseau et d’accélérer les accès aux données car la connexion n’est
ouverte et fermée qu’une seule fois pendant toute la durée de la session. Sans ce
paramétrage, chaque fermeture ou rétablissement de la connexion entraîne une
surcharge de travail liée au rattachement et détachement de la base de données.
Remarque Même si KeepConnections es à True, la fermeture de toutes les connexions de
bases de données actives reste à tout moment possible grâce à la méthode
DropConnections. Pour plus d’informations sur DropConnections, voir “Abandon
des connexions aux bases de données temporaires” à la page 16-9.
Par exemple, le code suivant interrompt les connexions inactives pour la session
par défaut :
Session.DropConnections;

Création, ouverture et fermeture des connexions de bases de


données
Pour ouvrir une connexion de base de données dans le cadre d’une session,
appelez la méthode OpenDatabase. Cette méthode accepte un paramètre : le nom
de la base de données à ouvrir. Ce nom est un alias BDE ou le nom d’un
composant base de données. Pour les tables Paradox ou dBASE, ce nom peut
mentionner un nom de chemin qualifié. Par exemple, l’instruction suivante utilise
la session par défaut et tente d’ouvrir une connexion de base de données pour la
base de données référencée par l’alias DBDEMOS :
var
DBDemosDatabase: TDatabase;
begin
DBDemosDatabase := Session.OpenDatabase('DBDEMOS');
...

Gestion de sessions de bases de données 16-7


Manipulation d’un composant session

OpenDatabase rend une session active si elle ne l’est pas déjà, puis détermine si le
nom de base de données spécifié correspond à la propriété DatabaseName d’un
composant base de données de cette session. Si le nom spécifié ne correspond à
aucun composant base de données existant, OpenDatabase crée un composant
base de données temporaire et lui donne ce nom. Chaque appel à OpenDatabase
incrémente d’une unité le nombre de références à la base de données. Tant que
ce nombre de références est supérieur à 0, la base de données est ouverte. Pour
finir, OpenDatabase appelle la méthode Open du composant base de données pour
établir la connexion au serveur.

Fermeture d’une connexion de base de données


Il est possible de fermer une connexion individuelle avec la méthode
CloseDatabase ou de fermer toutes les connexions en une seule opération avec la
méthode Close. Lorsque vous appelez CloseDatabase, le nombre de références
relatif à la base de données est réduit d’une unité. Lorsque le nombre de
références relatif à la base de données vaut 0, la base de données est fermée et
libérée. CloseDatabase prend un paramètre, le nom de la base de données à
fermer. Ainsi, l’instruction suivante ferme la connexion de base de données
ouverte dans l’exemple de la section précédente :
Session.CloseDatabase(DBDemosDatabase);
Si le nom de base de données spécifié est associé à un composant base de
données temporaire, et que la propriété KeepConnections de la session est à False,
le composant base de données temporaire est libéré, et la connexion est fermée.
Remarque Si KeepConnections est à False, les composants base de données temporaires sont
fermés et libérés automatiquement lorsque le dernier ensemble de données
associé au composant base de données est fermé. Pour obliger la fermeture, une
application peut toujours appeler CloseDatabase. Lorsque KeepConnections vaut
True, il est possible de libérer un composant base de données temporaire en
appelant sa méthode Close ou en appelant la méthode DropConnections de la
session.
Si le composant base de données est persistant (ce qui signifie que l’application
déclare spécifiquement le composant et l’instancie) et que la propriété
KeepConnections est à False, CloseDatabase appelle la méthode Close du composant
base de données pour fermer la connexion.
Remarque Le fait d’appeler CloseDatabase pour un composant base de données persistant ne
provoque pas la fermeture de la connexion. Pour fermer la connexion, appelez
directement la méthode Close du composant base de données.

Fermeture de toutes les connexions de base de données


Les deux opérations suivantes vous permettent de fermer toutes les connexions
de base de données en une seule fois :
• Mettez la propriété Active de la session à False.
• Appelez la méthode Close de la session.

16-8 Guide du développeur


Manipulation d’un composant session

Lorsque la propriété Active est mise à False, Delphi appelle automatiquement la


méthode Close. Cette méthode provoque la déconnexion de toutes les bases de
données actives en libérant les composants base de données temporaires et en
appelant la méthode Close de chaque composant base de données persistant.
Pour finir, Close met le handle BDE de la session à nil.

Abandon des connexions aux bases de données temporaires


Si la propriété KeepConnections d’une session vaut True (c’est la valeur par
défaut), les connexions des composants base de données temporaires sont
maintenues même si tous les ensembles de données utilisés par le composant
sont fermés. Il est possible d’éliminer ces connexions et de libérer tous les
composants base de données temporaires d’une session en appelant la méthode
DropConnections. Par exemple, le code suivant libère tous les composants base de
données temporaires inactifs de la session par défaut :
Session.DropConnections;
L’appel à la méthode DropConnections n’entraîne pas l’abandon ou la libération
des composants base de données temporaires pour lesquels un ou plusieurs
ensembles de données sont actifs. Pour libérer ces composants, vous devez
appeler la méthode Close.

Recherche de la connexion d’une base de données


La méthode FindDatabase d’une session permet de déterminer si un composant
base de données particulier est déjà associé à une session. FindDatabase accepte
un paramètre : le nom de la base de données à rechercher. Ce nom correspond à
un alias BDE ou au nom d’un composant base de données. Pour les tables
Paradox ou dBASE, ce nom peut référencer un nom de chemin qualifié.
La méthode FindDatabase renvoie le composant base de données trouvé. Si la
recherche échoue, elle renvoie nil.
Le code suivant recherche la session par défaut pour un composant base de
données utilisant l’alias DBDEMOS, Si elle n’est pas trouvée, elle est créée et
ouverte :
var
DB: TDatabase;
begin
DB := Session.FindDatabase('DBDEMOS');
if (DB = nil) then { database doesn't exist for session so,}
DB := Session.OpenDatabase('DBDEMOS'); { create and open it}
if Assigned(DB) and DB.Active then begin
DB.StartTransaction;
...
end;
end;

Gestion de sessions de bases de données 16-9


Manipulation d’un composant session

Extraction d’informations sur une session


Il est possible d’extraire des informations sur une session et ses composants base
de données en utilisant les méthodes informationnelles de la session. Ainsi, l’une
des méthodes permet d’extraire les noms de tous les alias connus dans la
session, et une autre permet d’obtenir les noms des tables associées à un
composant base de données de la session. Le tableau 16.1 dresse la liste des
méthodes informationnelles des composants session :

Tableau 16.1 Méthodes informationnelles des composants session


Méthode Utilisation
GetAliasDriverName Extrait le pilote BDE relatif à l’alias d’une base de données.
GetAliasNames Extrait la liste des alias BDE d’une base de données.
GetAliasParams Extrait la liste des paramètres d’un alias BDE de base de données.
GetConfigParams Extrait des informations de configuration du fichier de configuration
BDE.
GetDatabaseNames Extrait la liste des alias BDE et les noms des composants TDatabase
actuellement utilisés.
GetDriverNames Extrait les noms des pilotes BDE actuellement installés.
GetDriverParams Extrait la liste des paramètres d’un pilote BDE particulier.
GetStoredProcNames Extrait les noms de toutes les procédures stockées pour une base de
données particulière.
GetTableNames Extrait les noms de toutes les tables correspondant à un modèle
particulier pour une base de données spécifique.

A l’exception de GetAliasDriverName, ces méthodes renvoient un ensemble de


valeurs dans une liste de chaînes déclarée et gérée par votre application.
(GetAliasDriverName renvoie une seule chaîne, le nom du pilote BDE en cours
pour un composant base de données particulier utilisé par la session.)
Par exemple, le code suivant extrait les noms de tous les composants base de
données ainsi que les alias connus pour la session par défaut :
var
List: TStringList;
begin
List := TStringList.Create;
try
Session.GetDatabaseNames(List);
...
finally
List.Free;
end;
end;
Pour une description complète des méthodes informationnelles d’une session,
voir la rubrique TSession dans la référence d’aide en ligne de la VCL.

16-10 Guide du développeur


Manipulation d’un composant session

Manipulation des alias BDE


Du fait qu’une session encapsule généralement une série de connexions de bases
de données, le composant session offre une propriété et de nombreuses méthodes
pour manipuler les alias BDE. Chaque composant base de données associé à une
session possède un alias BDE (bien qu’un nom de chemin d’accès qualifié puisse
remplacer l’alias lors de l’accès à des tables Paradox ou dBASE). Les alias BDE et
les méthodes TSession associées poursuivent trois objectifs principaux :
• Déterminer la visibilité des alias
• Obtenir des informations sur les alias et les pilotes
• Créer, modifier et supprimer des alias
Les sections suivantes décrivent en détail ces différents points.

Spécification de la visibilité des alias


La propriété ConfigMode d’une session détermine quels sont les alias BDE visibles
pour la session. ConfigMode est un ensemble qui décrit quels types de sessions
sont visibles. La valeur par défaut est cmAll, qui est traduite dans l’ensemble
[cfmVirtual, cfmPersistent]. Si ConfigMode vaut cmAll, une session peut voir tous
les alias créés à l’intérieur d’elle, tous les alias du fichier de configuration BDE
d’un utilisateur et tous les alias gérés en mémoire par le BDE.
Le principal objectif de ConfigMode est de permettre à une application de
spécifier et de restreindre la visibilité des alias au niveau de la session. Par
exemple, le fait de mettre ConfigMode à cfmSession limite la “vue” de la session
aux alias créés dans le cadre de cette session. Les alias du fichier de
configuration du BDE et ceux en mémoire ne sont pas disponibles.
Pour une description complète de la propriété ConfigMode et de ses paramètres,
voir la référence en ligne relative aux bibliothèques de composants et d’objets.

Comment rendre des alias visibles aux autres sessions et applications


Lorsqu’un alias est créé dans le cadre d’une session, le BDE stocke une copie de
l’alias en mémoire. Par défaut, cette copie est uniquement locale pour la session
dans laquelle elle est créée. Une autre session de la même application peut voir
l’alias uniquement si sa propriété ConfigMode vaut cmAll ou cfmPersistent.
Pour rendre un alias disponible pour toutes les sessions et pour d’autres
applications, utilisez la méthode SaveConfigFile. SaveConfigFile place les alias en
mémoire dans le fichier de configuration du BDE où ils peuvent être lus et
utilisés par d’autres applications orientées BDE.

Comment déterminer les alias, les pilotes et les paramètres connus


Cinq méthodes relatives aux composants session permettent à une application
d’extraire des informations sur les alias BDE, y compris les informations relatives
aux paramètres et aux pilotes. Il s’agit des méthodes suivantes :
• GetAliasNames liste les alias auxquels une session peut accéder.
• GetAliasParams liste les paramètres de l’alias spécifié.

Gestion de sessions de bases de données 16-11


Manipulation d’un composant session

• GetAliasDriverName renvoie une chaîne contenant le nom du pilote BDE utilisé


par l’alias.
• GetDriverNames renvoie la liste de tous les pilotes BDE disponibles dans la
session.
• GetDriverParams renvoie les paramètres relatifs à un pilote particulier.
Pour plus d’informations sur l’utilisation des méthodes informationnelles d’une
session, voir “Extraction d’informations sur une session” à la page 16-10. Pour
plus d’informations sur les alias BDE, les paramètres et les pilotes, voir le fichier
d’aide en ligne BDE32.HLP.

Création, modification et suppression des alias


Une session peut créer, modifier et supprimer des alias. La méthode AddAlias
crée un nouvel alias BDE pour un serveur de base de données SQL.
AddStandardAlias crée un nouvel alias BDE pour des tables Paradox, dBASE ou
ASCII.
AddAlias prend trois paramètres : une chaîne contenant un nom d’alias, une
chaîne spécifiant le pilote SQL Links à utiliser, et une liste de chaînes contenant
des paramètres relatifs à l’alias. Pour plus d’informations sur AddAlias, voir la
rubrique relative à cette méthode dans la référence en ligne de la VCL. Pour plus
d’informations sur les alias BDE et sur les pilotes SQL Links, voir le fichier
d’aide en ligne du BDE, BDE32.HLP.
AddStandardAlias prend trois paramètres chaîne : le nom de l’alias, le chemin
d’accès qualifié aux tables Paradox ou dBASE et le nom du pilote par défaut à
utiliser pour les tentatives d’accès aux tables sans extension. Pour plus
d’informations sur AddStandardAlias, voir la référence en ligne des bibliothèques
d’objets et de composants. Pour plus d’informations sur les alias BDE, voir le
fichier d’aide en ligne du BDE, BDE32.HLP.
Remarque Lorsque vous ajoutez un alias dans une session, il est uniquement disponible
dans le cadre de cette session. Il peut être disponible pour d’autres sessions si
cfmPersistent est inclus dans les modes de configuration de la propriété
ConfigMode. Pour qu’un nouvel alias soit disponible pour toutes les applications,
appelez SaveConfigFile après avoir créé l’alias. Pour plus d’informations sur
ConfigMode, voir “Manipulation des alias BDE” à la page 16-11.
Une fois l’alias créé, il est possible de modifier ses paramètres en appelant
ModifyAlias. Cette méthode prend deux paramètres : le nom de l’alias à modifier
et une liste de chaînes contenant les paramètres à modifier et leurs valeurs.
Pour supprimer un alias précédemment créé dans une session, appelez la
méthode DeleteAlias. Cette méthode prend un paramètre : le nom de l’alias à
supprimer. DeleteAlias provoque l’indisponibilité de l’alias pour la session.
Remarque DeleteAlias ne supprime pas l’alias du fichier de configuration du BDE s’il a été
écrit dans le fichier suite à un appel à SaveConfigFile. Pour supprimer l’alias du
fichier de configuration après avoir appelé DeleteAlias, appelez SaveConfigFile une
nouvelle fois.

16-12 Guide du développeur


Manipulation d’un composant session

Les instructions suivantes utilisent AddAlias pour ajouter un nouvel alias pour
accéder à un serveur InterBase dans le cadre de la session par défaut :
var
AliasParams: TStringList;
begin
AliasParams := TStringList.Create;
try
with AliasParams do begin
Add('OPEN MODE=READ');
Add('USER NAME=TOMSTOPPARD');
Add('SERVER NAME=ANIMALS:/CATS/PEDIGREE.GDB');
end;
Session.AddAlias('CATS', 'INTRBASE', AliasParams);
...
finally
AliasParams.Free;
end;
end;
L’instruction suivante utilise AddStandardAlias pour créer un nouvel alias pour
accéder à une table Paradox :
AddStandardAlias('MYDBDEMOS', 'C:\TESTING\DEMOS\', 'Paradox');
Les instructions suivantes utilisent ModifyAlias pour provoquer le changement du
paramètre OPEN MODE en READ/ WRITE pour l’alias CATS dans la session
par défaut :
var
List: TStringList;
begin
List := TStringList.Create;
with List do begin
Clear;
Add('OPEN MODE=READ/WRITE');
end;
Session.ModifyAlias('CATS', List);
List.Free;
...

Déplacement parmi les composants base de données d’une


session
Les composants session disposent de deux propriétés qui vous permettent de
vous déplacer parmi les composants base de données associés à une session :
Databases et DatabaseCount.
Databases est un tableau des composants base de données actuellement actifs et
associés à une session. Utilisée avec la propriété DatabaseCount, Databases permet
de parcourir tous les composants base de données actifs afin d’effectuer une
action sélective ou globale.

Gestion de sessions de bases de données 16-13


Manipulation d’un composant session

DatabaseCount est une propriété de type entier qui indique le nombre de bases de
données actuellement actives associées à une session. Ce nombre évolue au fur et
à mesure des ouvertures et fermetures de connexions pendant la durée de la
session. Par exemple, si la propriété KeepConnections de la session est à False et
que, lors de l’exécution, tous les composants base de données sont créés au fur
et à mesure des besoins, alors, à chaque fois qu’une nouvelle base de données
est ouverte, DatabaseCount augmente d’une unité. De même, à chaque fois qu’une
base de données est fermée, DatabaseCount diminue d’une unité. Si DatabaseCount
est de valeur nulle, c’est qu’aucun composant base de données n’est actuellement
actif pour la session.
DatabaseCount est généralement utilisée conjointement à la propriété Databases
pour effectuer des actions communes à tous les composants base de données
actifs.
Le code suivant met à True la propriété KeepConnection de chaque base de
données active dans la session par défaut :
var
MaxDbCount: Integer;
begin
with Session do
if (DatabaseCount > 0) then
for MaxDbCount := 0 to (DatabaseCount - 1) do
Databases[MaxDbCount].KeepConnection := True;
end;

Spécification de l’emplacement des répertoires Paradox


Les composants session possèdent deux propriétés, NetFileDir et PrivateDir,
spécifiques aux applications manipulant des tables Paradox. NetFileDir spécifie le
répertoire qui contient le fichier Paradox de contrôle réseau PDOXUSRS.NET. Ce
fichier régit le partage des tables Paradox sur les unités de disque en réseau.
Toutes les applications partageant des tables Paradox doivent indiquer le même
répertoire pour le fichier de contrôle réseau (typiquement un répertoire sur un
serveur de fichier du réseau).
PrivateDir spécifie un répertoire de stockage temporaire des fichiers de traitement
des tables, comme ceux qui sont générés par le BDE pour gérer les instructions
SQL local.

Spécification de l’emplacement du fichier de contrôle


Delphi dérive la valeur de NetFileDir en interrogeant le fichier de configuration
du moteur de base de données Borland (BDE) pour l’alias de base de données
concerné. Si vous définissez NetFileDir vous-même, la valeur que vous fournissez
redéfinit le paramétrage du BDE ; assurez-vous donc de la validité de cette
nouvelle valeur.

16-14 Guide du développeur


Manipulation d’un composant session

Au moment de la conception, vous pouvez entrer dans l’inspecteur d’objets une


valeur pour NetFileDir. Vous pouvez aussi définir ou changer NetFileDir depuis
votre code au moment de l’exécution. Le code suivant définit NetFileDir pour la
session par défaut en lui affectant le répertoire à partir duquel s’exécute votre
application :
Session.NetFileDir := ExtractFilePath(Application.EXEName);
Remarque NetFileDir ne peut être changée que si votre application n’utilise aucun fichier
Paradox. Si vous modifiez NetFileDir au moment de l’exécution, vérifiez qu’elle
pointe sur un répertoire valide du réseau, partageable par tous les utilisateurs de
votre application.

Spécification de l’emplacement des fichiers temporaires


Si aucune valeur n’est spécifiée pour la propriété PrivateDir, le BDE utilise
automatiquement le répertoire en cours au moment de son initialisation. Si votre
application s’exécute à partir d’un serveur de fichiers en réseau, vous pouvez
définir PrivateDir pour qu’elle renvoie sur le disque local d’un utilisateur afin
d’augmenter les performances.
Remarque Il ne faut pas initialiser PrivateDir à la conception puis ouvrir la session dans
l’EDI. Cela génère une erreur signalant un répertoire occupé quand vous
exécutez l’application depuis l’EDI.
Le code suivant provoque le remplacement de la valeur de la propriété
PrivateDir de la session par défaut par le répertoire C:\TEMP d’un utilisateur :
Session.PrivateDir := 'C:\TEMP';
Important La propriété PrivateDir ne doit pas référencer le répertoire racine d’un disque.
Vous devez toujours spécifier un sous-répertoire.

Manipulation de tables Paradox et dBase protégées par mot de


passe
Les composants session disposent de quatre méthodes et d’un événement qui
sont exclusivement utilisés pour gérer l’accès aux fichiers Paradox et dBase
protégés par mot de passe. Il s’agit des méthodes AddPassword, GetPassword,
RemoveAllPasswords et RemovePassword. Quant à l’événement en question, il s’agit
de OnPassword. La fonction PasswordDialog permet également d'ajouter et de
supprimer un ou plusieurs mots de passe dans une session.

Utilisation de la méthode AddPassword


La méthode TSession.AddPassword fournit aux applications un moyen
supplémentaire pour ajouter un mot de passe à une session avant l’ouverture
d’une table Paradox ou dBase cryptée nécessitant un mot de passe. AddPassword
prend un paramètre : une chaîne contenant le mot de passe à utiliser. Vous
pouvez appeler AddPassword autant de fois que nécessaire pour ajouter des mots
de passe permettant d’accéder à des fichiers Paradox protégés par différents
mots de passe.

Gestion de sessions de bases de données 16-15


Manipulation d’un composant session

var
Passwrd: String;
begin
Passwrd := InputBox('Enter password', 'Password:', '');
Session.AddPassword(Passwrd);
try
Table1.Open
except
ShowMessage('Could not open table!');
Application.Terminate;
end;
end;
La fonction InputBox ci-dessus n'est utilisée qu'à titre de démonstration. Dans une
application réelle, utilisez des utilitaires de saisie de mot de passe qui masquent
le mot de passe lorsqu'il est entré, comme la fonction PasswordDialog ou une fiche
personnalisée. Sur une fiche personnalisée de saisie de mot de passe, utilisez un
composant TEdit en affectant un astérisque ("*") au paramètre PasswordChar.
Le bouton Ajouter de la boîte de dialogue de la fonction PasswordDialog produit
le même effet que la méthode AddPassword.
if PasswordDialog(Session) then
Table1.Open
else
ShowMessage('No password given, could not open table!');
end;
Remarque Il faut appeler AddPassword pour spécifier un ou plusieurs mots de passe à utiliser
(un à la fois) lors de l’accès à des fichiers protégés par mot de passe. Sinon,
lorsque votre application tente d’ouvrir une table Paradox protégée par mot de
passe, une boîte de dialogue demande à l’utilisateur de saisir un mot de passe.

Utilisation des méthodes RemovePassword et RemoveAllPasswords


TSession.RemovePassword supprime de la mémoire un mot de passe
précédemment ajouté. RemovePassword prend un paramètre : une chaîne
contenant le mot de passe à supprimer.
Session.RemovePassword(‘secret’);
TSession.RemoveAllPasswords supprime de la mémoire tous les mots de passe
précédemment ajoutés.
Session.RemoveAllPasswords;

Utilisation de la méthode GetPassword et de l’événement OnPassword


TSession.GetPassword déclenche l’événement TSession.OnPassword pour une session.
L’événement OnPassword est uniquement appelé lorsqu’une application tente
d’ouvrir une table Paradox ou dBase pour la première fois et que le BDE signale
que les droits d’accès sont insuffisants. Vous pouvez programmer un gestionnaire
d’événement OnPassword et fournir un mot de passe au BDE, ou bien vous
pouvez choisir d’utiliser la gestion de mot de passe par défaut (une boîte de
dialogue s’affiche pour demander à l’utilisateur de saisir un mot de passe).

16-16 Guide du développeur


Gestion de plusieurs sessions

Dans l'exemple suivant, le code désigne la procédure Password comme étant le


gestionnaire d'événement OnPassword en affectant le nom de la procédure à la
propriété TSession.OnPassword.
procedure TForm1.FormCreate(Sender: TObject);
begin
Session.OnPassword := Password;
end;
Dans la procédure Password, la fonction InputBox est utilisée pour demander un
mot de passe à l'utilisateur. La méthode TSession.AddPassword est utilisée pour
fournir par programmation le mot de passe entré dans la boîte de dialogue
d'ouverture de session.
procedure TForm1.Password(Sender: TObject; var Continue: Boolean);
var
Passwrd: String;
begin
Passwrd := InputBox('Enter password', 'Password:', '');
Continue := (Passwrd > '');
Session.AddPassword(Passwrd);
end;
La procédure Password est déclenchée par une tentative d'ouverture d'une table
protégée par mot de passe, comme montré ci-après. La gestion des exceptions
peut être utilisée pour traiter l'échec d'une tentative d'ouverture de la table.
Même si l'utilisateur est invité à entrer un mot de passe dans le gestionnaire
d'événement OnPassword, la tentative d'ouverture peut échouer si le mot de
passe est incorrect ou en cas d'un dysfonctionnement quelconque.
procedure TForm1.OpenTableBtnClick(Sender: TObject);
const
CRLF = #13 + #10;
begin
try
Table1.Open; { cette ligne déclenche l’événement OnPassword }
except
on E:Exception do begin { exception si ouverture impossible de la table }
ShowMessage('Error!' + CRLF + { affichage d’un message d’erreur avec explication }
E.Message + CRLF +
'Terminating application...');
Application.Terminate; { termine l’application }
end;
end;
end;

Gestion de plusieurs sessions


Si l’application que vous créez utilise plusieurs threads pour accomplir des
opérations de base de données, vous devrez créer une nouvelle session pour
chaque thread. La page AccèsBD de la palette des composants contient un
composant session que vous pouvez placer dans un module de données ou dans
une fiche au moment de la conception.

Gestion de sessions de bases de données 16-17


Gestion de plusieurs sessions

Important Quand vous placez un composant session, vous devez définir sa propriété
SessionName par une valeur unique pour qu’elle n’entre pas en conflit avec la
propriété SessionName de la session par défaut.
Le fait de créer un composant session au moment de la conception présuppose
que le nombre de threads (et donc de sessions) requis par l’application est
statique (déterminé à l’avance). Toutefois, le plus souvent, les sessions de votre
application devront être créées de façon dynamique. Pour créer dynamiquement
des sessions, il suffit d’appeler la fonction globale Sessions.OpenSession à
l’exécution.
Sessions.OpenSession accepte un paramètre : le nom de la session unique par
rapport aux autres noms de session de l’application. Le code suivant crée
dynamiquement une nouvelle session, puis l’active à partir d’un nom unique
généré :
Sessions.OpenSession('RunTimeSession' + IntToStr(Sessions.Count + 1));
Cette instruction génère un nom unique pour la nouvelle session en récupérant
le nombre actuel de sessions puis en ajoutant un à cette valeur. Remarquez que
cet exemple de code ne fonctionnera pas si vous créez et détruisez des sessions
de façon dynamique à l’exécution. Néanmoins, cet exemple montre comment
utiliser les propriétés et les méthodes de Sessions pour gérer plusieurs sessions.
Sessions est une variable de type TSessionList qui est automatiquement instanciée
dans une application de base de données. Les propriétés et les méthodes Sessions
sont utilisées pour garder la trace des sessions dans une application de base de
données multithread. Le tableau 16.2 dresse la liste des propriétés et des
méthodes du composant TSessionList :

Tableau 16.2 Méthodes et propriétés de TSessionList


Propriété
ou méthode Rôle
Count Renvoie le nombre de sessions, actives et inactives, dans la liste des
sessions.
FindSession Recherche le nom de session spécifié dans la liste des sessions, en
renvoyant un pointeur sur le composant session ou nil si aucune session
ne porte le nom indiqué. Si un nom de session vide est transmis,
FindSession renvoie un pointeur sur la session par défaut (Session).
GetSessionNames Remplit une liste de chaînes avec le nom de tous les composants session
actuellement instanciés. Cette procédure ajoute toujours au minimum la
chaîne correspondant à la session par défaut (notez que le nom de la
session par défaut est en fait une chaîne vide).
List Renvoie le composant session correspondant au nom de session spécifié.
Une exception est déclenchée si aucune session ne porte ce nom.
OpenSession Crée, puis active une nouvelle session ou bien réactive une session
existante à partir du nom de session spécifié.
Sessions Permet d’accéder à la liste des sessions par numéro d’ordre dans la liste.

16-18 Guide du développeur


Utilisation d’un composant session dans des modules de données

Comme exemple d’utilisation des propriétés et des méthodes de Sessions dans


une application multithread, voyons de plus près ce qui se produit lorsque vous
voulez ouvrir une connexion de base de données. Pour déterminer si une
connexion existe déjà, utilisez la propriété Sessions afin de parcourir chaque
session dans la liste des sessions en partant de la session par défaut. Pour
chaque composant session, vous devez examiner sa propriété Databases pour voir
si la base de données recherchée est ouverte. Si vous découvrez qu’un autre
thread utilise déjà cette base de données, passez à la session suivante dans la
liste.
Si un thread existant n’utilise pas la base de données, vous pouvez ouvrir la
connexion à l’intérieur de cette session.
Par contre, si tous les threads utilisent la base de données, vous devez d’abord
ouvrir une nouvelle session pour ouvrir une autre connexion vers la base de
données.
Si vous répliquez un module de données contenant une session dans une
application multithread, dans laquelle chaque thread contient sa propre copie du
module de données, vous pouvez utiliser la propriété AutoSessionName pour faire
en sorte que tous les modules de données utilisent la session adéquate.
L'affectation de AutoSessionName à True amène la session à générer
dynamiquement son propre nom unique lorsqu'elle est créée à l'exécution. Puis
ce nom est affecté à chaque ensemble de données dans le module de données,
écrasant ainsi tous les noms de session explicitement définis. Ainsi, chaque
thread dispose de sa propre session et chaque ensemble de données utilise la
session figurant dans son propre module de données.

Utilisation d’un composant session dans des modules de


données
Vous pouvez placer sans problème des composants session dans vos modules de
données. Toutefois, si vous placez dans le référentiel d’objets un module de
données contenant un ou plusieurs composants session, assurez-vous que leur
propriété AutoSessionName est à True pour éviter les conflits relatifs au domaine
d’appellation global lorsque d’autres utilisateurs souhaiteront procéder à un
héritage.

Gestion de sessions de bases de données 16-19


16-20 Guide du développeur
Chapitre

Connexion aux bases de données


Chapter 17
17
Lorsqu’une application Delphi utilise le BDE (Borland Database Engine) pour se
connecter à une base de données, la connexion est encapsulée dans un
composant TDatabase. En fait, le composant base de données encapsule la
connexion à une seule base de données dans le contexte d’une session BDE. Ce
chapitre décrit les composants base de données et comment manipuler les
connexions aux bases de données.
Les composants base de données sont également utilisés pour gérer les
transactions dans les applications basées sur le BDE. Pour plus d'informations
sur l'utilisation des bases de données pour gérer les transactions, voir “Utilisation
des transactions” à la page 13-5.
Les composants base de données permettent aussi d'appliquer les mises à jour
mises en mémoire cache aux tables reliées. Pour plus d'informations sur
l'utilisation d'un composant base de données pour appliquer les mises à jour
mises en mémoire cache, voir “Utilisation de la méthode d’un composant base
de données” à la page 25-6.

Présentation des composants base de données persistants et


temporaires
Dans une application de base de données, chaque connexion BDE est encapsulée
par un composant base de données qu’il soit créé explicitement en mode
conception ou dynamiquement à l’exécution. Lorsqu’une application tente de se
connecter à une base de données, elle utilise un composant base de données
explicitement instancié (aussi appelé composant persistant) ou génère un
composant base de données temporaire qui n’existe que pour la durée de la
connexion.

Connexion aux bases de données 17-1


Présentation des composants base de données persistants et temporaires

Les composants base de données temporaires sont générés au fur et à mesure


des besoins pour tout ensemble de données d’un module ou d’une fiche pour
lequel vous n’avez pas créé de composant base de données. Les composants base
de données temporaires supportent de nombreuses applications de bases de
données de bureau sans que vous ayez à gérer les détails de la connexion aux
bases de données. Toutefois, pour la plupart des applications client/serveur,
nous vous recommandons de créer vos propres composants base de données. En
effet, les composants base de données persistants procurent un plus grand
contrôle sur les bases de données et apportent les possibilités suivantes :
• Création de connexions persistantes aux bases de données.
• Personnalisation des connexions aux serveurs de bases de données.
• Contrôle des transactions et spécification des niveaux d’isolement des
transactions.
• Création d’alias BDE locaux à l’application.

Utilisation des composants base de données temporaires


Les composants base de données temporaires sont automatiquement générés au
fur et à mesure de ses besoins. Ainsi, si vous placez un composant TTable sur
une fiche, définissez ses propriétés et ouvrez la table sans avoir préalablement
placé et configuré un composant TDatabase, Delphi crée un composant base de
données temporaire en arrière-plan.
Quelques-unes des propriétés clé des composants base de données temporaires
sont définies par la session à laquelle ils appartiennent. Par exemple, la propriété
KeepConnections de la session de contrôle détermine si la connexion vers la base
de données est maintenue après que tous les ensembles de données associés
aient été fermés (comportement par défaut), ou si elle est interrompue. De même,
l’événement par défaut OnPassword d’une session assure l’affichage de la boîte
de dialogue standard demandant de saisir un mot de passe lorsque la session
tente de se rattacher à une base de données située sur un serveur protégé par
mot de passe. D’autres propriétés des composants base de données temporaires
assurent la gestion standard des procédures de connexion et des transactions.
Pour plus d’informations concernant les sessions et le contrôle des sessions opéré
sur les connexions de base de données, voir “Manipulation d’un composant
session” à la page 16-2.
Les propriétés par défaut créées pour les composants base de données
temporaires assurent un comportement générique standard permettant de
prendre en compte une grande variété de situations. Néanmoins, dans le cas
d’une application client/serveur ayant une mission critique ou faisant intervenir
de nombreux utilisateurs avec des contraintes spécifiques sur les connexions de
base de données, vous devrez créer vos propres composants base de données
pour affiner le réglage de chaque connexion de base de données en fonction des
spécificités de votre application.

17-2 Guide du développeur


Présentation des composants base de données persistants et temporaires

Création de composants base de données en mode conception


La page AccèsBD de la palette des composants contient un composant base de
données que vous pouvez placer dans un module de données ou dans une fiche.
Le principal avantage que procure la création d’un composant base de données
au moment de la conception réside dans la possibilité que vous avez de définir
la valeur initiale de ses propriétés et d’écrire un OnLogin. Ce dernier permet de
personnaliser la gestion des sécurités du serveur de base de données au moment
du rattachement initial du composant base de données vers le serveur. Pour plus
d’informations sur la gestion des propriétés de connexion, voir “Connexion à un
serveur de bases de données” à la page 17-7. Pour plus d’informations
concernant la sécurité des serveurs, voir “Contrôle de la connexion au serveur” à
la page 17-7.

Création de composants base de données à l’exécution


Vous pouvez également créer des composants base de données à l’exécution.
Une application peut procéder ainsi quand le nombre de composants base de
données nécessaires à l’exécution est indéterminé et que votre application doit
contrôler explicitement la connexion de base de données. En fait, Delphi crée à
l’exécution des composants base de données temporaires au fur et à mesure des
besoins. Quand vous créez un composant base de données à l’exécution, vous
devez lui donner un nom unique avant de l’associer à une session.
Le composant n’est réellement créé que lorsque vous appelez le constructeur
TDatabase.Create. En partant d’un nom de base de données et d’un nom de
session, la fonction suivante crée un composant base de données, l’associe à une
session (en créant une nouvelle session le cas échéant), puis définit quelques-
unes de ses propriétés essentielles :
function TDataForm.RunTimeDbCreate(const DatabaseName, SessionName: String): TDatabase;
var
TempDatabase: TDatabase;
begin
TempDatabase := nil;
try
{ Activation de la session si elle existe ; sinon, création d’une nouvelle session}
Sessions.OpenSession(SessionName);
with Sessions do
with FindSession(SessionName) do begin
Result := FindDatabase(DatabaseName);
if (Result = nil) then begin
{ Création d’un nouveau composant base de données }
TempDatabase := TDatabase.Create(Self);
TempDatabase.DatabaseName := DatabaseName;
TempDatabase.SessionName := SessionName;
TempDatabase.KeepConnection := True;
end;
Result := OpenDatabase(DatabaseName);
end;

Connexion aux bases de données 17-3


Contrôle des connexions

except
TempDatabase.Free;
raise;
end;
end;
Le fragment de code suivant illustre comment cette fonction peut être appelée à
l’exécution afin de créer un composant base de données pour la session par
défaut :
var
MyDatabase: array [1..10] of TDatabase;
MyDbCount: Integer;
begin
{ Initialise MyDbCount ultérieurement }
MyDbCount := 1;
ƒ
{ Puis crée un composant base de données à l’exécution }
begin
MyDatabase[MyDbCount] := RunTimeDbCreate('MyDb' + IntToStr(MyDbCount), '');
Inc(MyDbCount);
end;
ƒ
end;

Contrôle des connexions


Qu’un composant base de données soit créé au moment de la conception ou à
l’exécution, vous pouvez utiliser les propriétés, les événements et les méthodes du
composant TDatabase pour changer son comportement. Les sections suivantes
décrivent comment manipuler les composants base de données. Pour plus de
détails sur les propriétés, les événements et les méthodes de TDatabase, voir dans
l’aide en ligne les références relatives aux bibliothèques de composants et d’objets.

Association d’un composant base de données à une session


Tous les composants base de données doivent être associés à une session BDE.
Ils possèdent deux propriétés, Session et SessionName, qui permettent d’établir
cette association.
SessionName identifie l’alias de la session auquel un composant base de données
doit être associé. Lors de la création d’un composant base de données en mode
conception, SessionName prend la valeur “Default”. Les applications multithreads
ou les applications BDE réentrantes peuvent comporter plusieurs sessions. En
mode conception, vous pouvez choisir une valeur valide pour la propriété
SessionName dans la liste déroulante de l’inspecteur d’objets. Les noms de session
apparaissant dans la liste proviennent des propriétés SessionName de chaque
composant session de l’application.

17-4 Guide du développeur


Contrôle des connexions

La propriété Session est une propriété en lecture seule, disponible à l’exécution,


qui pointe sur le composant session référencé par la propriété SessionName. Par
exemple, si la propriété SessionName est vierge ou vaut “Default”, la propriété
Session pointe sur la même instance TSession référencée par la variable globale
Session. Session permet aux applications d’accéder aux propriétés, événements et
méthodes du composant session parent du composant base de données sans qu’il
soit nécessaire de connaître le nom de la session. Ceci peut être utile lorsqu’un
composant base de données est affecté à une autre session à l’exécution.
Pour plus d’informations sur les sessions BDE, voir chapitre 16, “Gestion de
sessions de bases de données”.

Spécification d’un alias BDE


AliasName et DriverName sont des propriétés spécifiques au BDE qui s’excluent
mutuellement. AliasName spécifie le nom d’un alias BDE à utiliser avec le
composant base de données. L’alias apparaît dans les listes déroulantes des
composants ensemble de données de façon à vous permettre de lier ces
composants à un composant base de données particulier. Si vous spécifiez
AliasName pour un composant base de données, toute valeur déjà affectée à
DriverName est effacée car un nom de pilote fait toujours partie d’un alias BDE.
Remarque Les alias BDE sont créés et édités en utilisant l’explorateur de bases de données
ou l’administrateur BDE. Pour davantage d’informations sur la création et la
gestion des alias BDE, voir la documentation en ligne de ces utilitaires.
DatabaseName fournit un nom alternatif pour un composant base de données. Le
nom fourni qui se rajoute à AliasName ou DriverName est local à votre
application. DatabaseName peut être un alias BDE, ou, pour les fichiers Paradox et
dBASE, un nom de chemin d’accès qualifié. Comme AliasName, DatabaseName
apparaît dans les listes déroulantes des composants ensemble de données pour
vous permettre de les lier à un composant base de données.
DriverName est le nom d’un pilote BDE. Le nom du pilote est l’un des
paramètres d’un alias BDE, mais il est possible de spécifier un nom de pilote au
lieu d’un alias, quand vous créez un alias BDE local pour un composant base de
données en utilisant la propriété DatabaseName. Si vous spécifiez DriverName,
toute valeur déjà affectée à AliasName est effacée pour éviter des conflits
éventuels entre le nom du pilote que vous avez spécifié et celui qui fait partie de
l’alias BDE, identifié par AliasName.
Pour spécifier un alias BDE, affecter un pilote BDE, ou bien créer un alias BDE
local lors de la conception, double-cliquez sur un composant base de données
afin d’appeler l’éditeur des propriétés de base de données.
Vous pouvez saisir la propriété DatabaseName dans la boîte de saisie “Nom” de
l’éditeur des propriétés. Vous pouvez saisir un nom d’alias BDE existant dans la
boîte à options “Nom d’alias” pour la propriété Alias, ou choisir un alias existant
dans la liste déroulante. La boîte à options Nom de pilote vous permet de saisir
le nom d’un pilote BDE existant pour la propriété DriverName. Vous avez
également la possibilité de choisir un nom de pilote existant dans la liste
déroulante.

Connexion aux bases de données 17-5


Contrôle des connexions

Remarque L’éditeur des propriétés de base de données permet aussi de visualiser et de


définir les paramètres de connexion BDE et les états des propriétés LoginPrompt
et KeepConnection. Pour travailler avec des paramètres de connexion, voir
“Définition des paramètres des alias BDE” à la page 17-6. Pour définir l’état de
LoginPrompt, voir “Contrôle de la connexion au serveur” à la page 17-7, et pour
définir KeepConnection, voir “Connexion à un serveur de bases de données” à la
page 17-7.
Pour définir DatabaseName, AliasName ou DriverName lors de l’exécution, incluez
l’instruction d’affectation appropriée dans votre code. Par exemple, le code
suivant récupère le texte d’une boîte de saisie afin de créer un alias local pour le
composant base de données Database1 :
Database1.DatabaseName := Edit1.Text;

Définition des paramètres des alias BDE


Au moment de la conception, vous pouvez créer ou modifier des paramètres de
connexion de trois façons différentes :
• En utilisant l’explorateur de bases de données ou l’administrateur BDE pour
créer ou modifier les alias BDE, y compris leurs paramètres. Pour plus
d’informations sur ces utilitaires, voir les fichiers d’aide en ligne.
• En double-cliquant sur la propriété Params dans l’inspecteur d’objets afin
d’appeler l’éditeur de listes de chaînes. Pour en savoir plus sur l’éditeur de
listes de chaînes, voir “Manipulation des listes de chaînes” dans l’aide en ligne.
• En double-cliquant sur le composant base de données dans un module de
données ou dans une fiche pour appeler l’éditeur des propriétés de base de
données.
Toutes ces méthodes ont pour effet d’éditer la propriété Params du composant
base de données. La propriété Params est une liste de chaînes contenant les
paramètres de connexion de base de données de l’alias BDE associé au composant
base de données. Parmi les paramètres de connexion standard, il y a l’instruction
path, le nom du serveur, le pilote de langue et le mode de requête SQL.
Quand vous appelez pour la première fois l’éditeur des propriétés de base de
données, les paramètres relatifs à l’alias BDE n’apparaissent pas. Pour voir les
paramètres d’un pilote ou d’un alias sélectionné, cliquez sur Défauts. Les
paramètres en cours sont affichés dans la boîte mémo Remplacements paramètre.
Vous pouvez modifier les entrées existantes ou en ajouter de nouvelles. Pour
effacer les paramètres existants, cliquez sur Effacer. Les modifications apportées
prennent effet dès que vous cliquez sur OK.
A l’exécution, une application peut définir des paramètres d’alias en modifiant
directement la propriété Params. Pour plus d’informations sur les paramètres
propres aux pilotes SQL Links, voir le fichier d’aide en ligne SQL.

17-6 Guide du développeur


Contrôle des connexions

Contrôle de la connexion au serveur


La plupart des serveurs de base de données distants incluent une gestion de la
sécurité pour empêcher les accès non autorisés. Généralement, le serveur
demande un nom d’utilisateur et un mot de passe lors de la procédure de
connexion avant d’autoriser l’accès à une base de données.
Lors de la conception, si un serveur requiert une procédure de connexion, une
boîte de dialogue de connexion standard demande de saisir un nom d’utilisateur
et un mot de passe au moment de la première tentative de connexion à la base
de données.
A l’exécution, il y a trois moyens de gérer la procédure de connexion d’un serveur :
• En mettant la propriété LoginPrompt d’une base de données à True (la valeur
par défaut). Votre application affiche la boîte de dialogue standard de
connexion lorsque le serveur attend un nom d’utilisateur et un mot de passe.
• En définissant la propriété LoginPrompt à False, et en codant en dur les
paramètres USER NAME et PASSWORD dans la propriété Params du
composant base de données. Par exemple :
USER NAME=SYSDBA
PASSWORD=masterkey
Important Notez que la propriété Params étant facile à visualiser, cette méthode
compromet sérieusement la sécurité du serveur et il n’est donc pas
recommandé de l’utiliser.
• Ecrivez un événement OnLogin pour le composant base de données puis
utilisez-le pour définir les paramètres de connexion à l’exécution. OnLogin
récupère une copie de la propriété Params du composant base de données que
vous pouvez ensuite modifier. LoginParams est le nom de la copie dans
OnLogin. Utilisez la propriété Values pour définir ou changer les paramètres de
connexion de la façon suivante :
LoginParams.Values['USER NAME'] := UserName;
LoginParams.Values['PASSWORD'] := PasswordSearch(UserName);
Lorsqu’il achève son exécution, OnLogin transmet ses valeurs LoginParams à
Params, ces paramètres étant utilisés pour établir la connexion.

Connexion à un serveur de bases de données


Deux méthodes permettent de se connecter à un serveur de bases de données en
utilisant un composant base de données :
• Appeler la méthode Open.
• Mettre à True la propriété Connected.
Le fait de mettre Connected à True provoque l’exécution de la méthode Open.
Cette méthode vérifie que la base de données spécifiée par la propriété
DatabaseName ou Directory existe et qu’un événement OnLogin existe pour le
composant base de données. Si c’est le cas, la connexion est effectuée. Sinon, la
boîte de dialogue de connexion par défaut apparaît.

Connexion aux bases de données 17-7


Contrôle des connexions

Remarque Lorsqu’un composant base de données n’est pas connecté à un serveur et qu’une
application tente d’ouvrir un ensemble de données associé à un composant base
de données, la méthode Open du composant base de données est appelée pour
établir la connexion. Si l’ensemble de données n’est pas associé à un composant
base de données existant, un composant base de données temporaire est créé et
utilisé pour établir la connexion.
Une fois établie, la connexion de base de données est conservée tant qu’il y a au
moins un ensemble de données actif. Si aucun ensemble de données n’est plus
actif, l’abandon ou le maintien de la connexion dépend de la propriété
KeepConnection du composant base de données.
KeepConnection détermine si votre application doit maintenir la connexion à une
base de données même si tous les ensembles de données associés à cette base
sont fermés. Si elle est à True, la connexion est maintenue. Pour les connexions
vers des serveurs de base de données distants, ou pour les applications qui
ouvrent et ferment fréquemment des ensembles de données, il est préférable que
KeepConnection soit à True afin de réduire le trafic sur le réseau et d’accélérer
votre application. Si elle est à False (la valeur par défaut), une connexion est
fermée dès qu’il n’y a plus d’ensemble de données actif utilisant la base de
données. Si un ensemble de données (qui utilise la base de données) est
ultérieurement ouvert, la connexion doit être à nouveau établie et initialisée.

Considérations relatives à la connexion à un serveur distant


Lorsque vous vous connectez à un serveur de base de données distant à partir
d’une application, l’application utilise le BDE et le pilote SQL Links de Borland
pour établir la connexion. Le BDE peut communiquer via un pilote ODBC que
vous fournissez vous-même. Avant d’établir une connexion, vous devez
configurer le pilote SQL Links ou ODBC en fonction de votre application. Les
paramètres SQL Links et ODBC sont stockés dans la propriété Params du
composant base de données. Pour plus d’informations concernant les paramètres
SQL Links, voir le guide de l’utilisateur en ligne de SQL Links. Pour modifier la
propriété Params, voir “Définition des paramètres des alias BDE” à la page 17-6.

Utilisation des protocoles réseau


Comme partie intégrante de la configuration d’un pilote SQL Links ou ODBC, il
est nécessaire de spécifier le protocole réseau utilisé par le serveur (par exemple
SPX/IPX ou TCP/IP, selon les options de configuration du pilote). Dans la
plupart des cas, la configuration du protocole réseau est gérée par le logiciel de
configuration du client. Pour ODBC, il peut être nécessaire de vérifier la
configuration du pilote en utilisant un gestionnaire de pilote ODBC.
L’établissement d’une connexion initiale entre client et serveur peut être
problématique. La liste de questions suivante devrait vous être utile si vous
rencontrez des difficultés :
• La connexion côté client du serveur est-elle correctement configurée ?
• Si vous utilisez TCP/IP :
• Le logiciel de communication TCP/IP est-il installé ? La bibliothèque
WINSOCK.DLL appropriée est-elle installée ?

17-8 Guide du développeur


Contrôle des connexions

• L’adresse IP du serveur est-elle recensée dans le fichier HOSTS du client ?


• Le DNS (Domain Name Services) est-il correctement configuré ?
• Pouvez-vous établir une connexion avec le serveur ?
• Les DLL de vos connexions et de vos pilotes de base de données se trouvent-
elles dans votre chemin d’accès (PATH) ?
Pour des informations plus complètes sur le diagnostic d’erreurs, voir le guide de
l’utilisateur en ligne de SQL Links ainsi que la documentation de votre serveur.

Utilisation de ODBC
Une application peut utiliser les sources de données ODBC telles que Btrieve.
Une connexion de pilote ODBC nécessite :
• Un pilote ODBC fourni par votre revendeur.
• Le gestionnaire Microsoft de pilote ODBC.
• L’administrateur BDE.
Pour définir un alias BDE pour une connexion de pilote ODBC, utilisez
l’administrateur BDE. Pour plus d’informations, voir le fichier d’aide en ligne de
l’administrateur BDE.

Déconnexion d’un serveur de base de données


Deux méthodes permettent de se déconnecter d’un serveur à partir d’un
composant base de données :
• Mettre la propriété Connected à False.
• Appeler la méthode Close.
Le fait de mettre Connected à False appelle Close. Close ferme tous les ensembles
de données ouverts et provoque la déconnexion du serveur. Par exemple, le code
suivant ferme tous les ensembles de données actifs d’un composant base de
données et abandonne ses connexions :
Database1.Connected := False;
Remarque Close provoque la déconnexion d’un serveur de base de données même si la
propriété KeepConnection est à True.

Fermeture d’ensembles de données sans déconnexion du serveur


Les ensembles de données doivent parfois être fermés sans pour autant mettre
fin à la connexion au serveur de base de données. Pour fermer tous les
ensembles de données ouverts sans provoquer la déconnexion du serveur,
procédez comme suit :
1 Mettez la propriété KeepConnection du composant base de données à True.
2 Appelez la méthode CloseDataSets du composant base de données.

Connexion aux bases de données 17-9


Interactions entre les composants base de données et les composants session

Déplacement parmi les ensembles de données d’un composant


base de données
Deux propriétés du composant base de données permettent à une application de
parcourir tous les ensembles de données associés au composant. Il s’agit des
propriétés DataSets et DataSetCount.
DataSets est un tableau indicé de tous les ensembles de données actifs (TTable,
TQuery et TStoredProc) d’un composant base de données. L’ensemble de données
actif est celui qui est actuellement ouvert. DataSetCount est une valeur entière
accessible en lecture uniquement qui spécifie le nombre d’ensembles de données
actuellement actif.
Utilisez DataSets avec DataSetCount pour effectuer un cycle sur tous les
ensembles de données actuellement actifs depuis votre code. Par exemple, le
code suivant parcourt tous les ensembles de données actifs afin de basculer à
True la propriété CachedUpdates de tous les ensembles de données de type
TTable :
var
I: Integer;
begin
for I := 0 to DataSetCount - 1 do
if DataSets[I] is TTable then
DataSets[I].CachedUpdates := True;
end;

Interactions entre les composants base de données et les


composants session
Généralement, les propriétés des composants TSession, comme KeepConnection,
définissent les comportements par défaut qui s’appliquent à tous les composants
de base de données temporaires créés à l’exécution en fonction des besoins.
Les méthodes s’appliquent de façon différente. Les méthodes TSession affectent
tous les composants base de données, quel que soit l’état de ces composants. Par
exemple, la méthode DropConnections du composant session ferme tous les
ensembles de données appartenant aux composants base de données d’une
session, puis interrompt toutes les connexions de bases de données, même si la
propriété KeepConnection d’un composant base de données spécifique est à True.
Les méthodes des composants TDatabase s’appliquent uniquement aux ensembles
de données associés à un composant base de données. Par exemple, supposons
que le composant base de données Database1 soit associé à la session par défaut.
Database1.CloseDataSets() ferme uniquement les ensembles de données associés à
Database1. Les ensembles de données ouverts qui appartiennent à d’autres
composants base de données à l’intérieur de la session par défaut restent quant à
eux ouverts.

17-10 Guide du développeur


Utilisation de composants base de données dans des modules de données

Utilisation de composants base de données dans des modules


de données
Vous pouvez placer sans problème des composants base de données dans vos
modules de données. Toutefois, pour que les utilisateurs puissent hériter d’un
module de données contenant un composant base de données placé dans le
référentiel d’objets, il faut affecter la valeur True à la propriété HandleShared afin
d’empêcher les conflits dans l’espace global de nom.

Exécution d’instructions SQL depuis un composant TDatabase


Des instructions SQL simples peuvent être exécutées directement depuis un
composant TDatabase à l’aide de sa méthode Execute. La méthode Execute a été
conçue principalement pour exécuter des instructions SQL en DDL (langage de
définition des données). Ce sont des instructions qui ne renvoient pas
d’ensembles de résultats et qui opèrent uniquement sur les métadonnées d’une
base de données. La méthode Execute peut cependant être utilisée pour exécuter
des instructions SQL en DML (langage de manipulation des données) qui
renvoient des ensembles de résultats et opèrent sur les données stockées dans les
tables d’une base de données.
Utilisez la méthode Execute pour exécuter une instruction SQL à la fois. Il n’est
pas possible d’exécuter plusieurs instructions SQL à l’aide d’un seul appel à
Execute, comme avec les utilitaires de script SQL. Pour exécuter plusieurs
instructions : remplacez le contenu du paramètre SQL par une nouvelle
instruction et rappelez la méthode Execute ; répétez ces deux étapes autant de
fois qu’il y a d’instructions à exécuter. Selon les cas, il sera peut être nécessaire
ou approprié d’effectuer également d’autres modifications, comme changer la
valeur des paramètres entre les exécutions.

Exécution d’instructions SQL sans ensemble de résultats


Les instructions SQL en DDL (langage de définition des données) et certaines
instructions en DML (langage de manipulation des données) ne produisent pas
d’ensemble de résultats. Ces instructions sont exécutées, affectent les
métadonnées (instructions DDL) ou les données (instructions DML), puis arrêtent
toute interaction avec la base de données. Voici des exemples d’instructions
DDL : CREATE INDEX, ALTER TABLE et DROP DOMAIN. Voici des exemples
d’instructions DML qui opèrent sur les données mais ne renvoient pas
d’ensemble de résultats : INSERT, DELETE et UPDATE.
Pour exécuter ces instructions qui ne produisent pas d’ensemble de résultats,
appelez la méthode TDatabase.Execute. L’instruction SQL à exécuter est
représentée par une valeur String (variable ou littérale) et utilisée comme
paramètre SQL de la méthode Execute. Si aucun paramètre n’est utilisé dans
l’instruction SQL, passez la valeur nil comme paramètre Params de Execute.

Connexion aux bases de données 17-11


Exécution d’instructions SQL depuis un composant TDatabase

Pour plus d’informations sur l’utilisation des paramètres avec la méthode


Execute, voir “Exécution d’instructions SQL paramétrées” à la page 17-13. Comme
ces instructions ne renvoient pas d’ensemble de résultats, passez la valeur nil
comme paramètre Cursor de Execute.
Dans l’exemple suivant, une instruction CREATE TABLE (DDL) sans paramètre
est exécutée à l’aide de la méthode Execute.
procedure TDataForm.CreateTableButtonClick(Sender: TObject);
var
SQLstmt: String;
begin
Database1.Connected := True;
SQLstmt := 'CREATE TABLE NewCusts ' +
'( ' +
' CustNo INTEGER, ' +
' Company CHAR(40), ' +
' State CHAR(2), ' +
' PRIMARY KEY (CustNo) ' +
')';
Database1.Execute(SQLstmt, nil, False, nil);
end;
Dans l’exemple suivant, une instruction INSERT (DDL) sans paramètre et ne
renvoyant pas d’ensemble de résultats est exécutée.
procedure TDataForm.InsertRecordButtonClick(Sender: TObject);
var
SQLstmt: String;
begin
Database1.Connected := True;
SQLstmt := 'INSERT INTO Customer ' +
'(Custno, Company, State) ' +
'VALUES (9999, "John Doe Rentals", "CA")';
Database1.Execute(SQLstmt, nil, False, nil);
end;

Exécution d’instructions SQL avec ensembles de résultats


Seules les instructions SQL en DML (langage de manipulation des données)
renvoient des ensembles de résultats. Et encore, seule une requête DML utilisant
l’instruction SELECT produit un ensemble de résultats. (L’instruction SELECT
s’adresse à une table d’une base de données ou à une procédure stockée qui
renvoie un ensemble de résultats.)
Pour exécuter ces instructions produisant un ensemble de résultats, appelez la
méthode TDatabase.Execute et fournissez un composant ensemble de données
pour l’ensemble de résultats. L’instruction SQL à exécuter est représentée par
une valeur String (variable ou littérale) et utilisée comme paramètre SQL de la
méthode Execute. Si aucun paramètre n’est utilisé dans l’instruction SQL, passez
la valeur nil comme paramètre Params de Execute. Pour plus d’informations sur
l’utilisation des paramètres avec la méthode Execute, voir “Exécution
d’instructions SQL paramétrées” à la page 17-13.

17-12 Guide du développeur


Exécution d’instructions SQL depuis un composant TDatabase

Pour que l’ensemble de résultats produit par l’instruction SQL soit disponible
après l’appel à la méthode Execute, fournissez au préalable un composant ensemble
de données (par exemple un composant TTable). Fournissez une variable de type
hDBICur (un handle sur un curseur BDE). (Pour utiliser ce type de données, l’unité
contenant l’appel à la méthode Execute doit inclure “BDE”, l’unité enveloppe BDE,
dans sa section Uses.) Passez cette variable hDBICur (dé-référencée) dans le
paramètre Cursor de la méthode Execute. Une fois que la méthode Execute est
appelée, affectez le handle du curseur BDE à la propriété Handle du composant
ensemble de données. (Le composant ensemble de données doit être transtypé en
TDBDataSet pour cette affectation, car la propriété Handle est en lecture seule dans
toutes les classes liées aux ensembles de données sauf dans TDBDataSet.)
L’exemple de routine ci-après exécute une instruction SELECT à l’aide de la
méthode Execute. L’ensemble de résultats produit par cette action est accessible
via un composant TTable nommé Table1.
procedure TDataForm.SELECT_NoParamsButtonClick(Sender: TObject);
var
SQLstmt: String;
Cursor: hDBICur;
begin
Database1.Connected := True;
SQLstmt := 'SELECT Company, State ' +
'FROM Customer ' +
'ORDER BY State, Company';
Database1.Execute(SQLstmt, nil, False, @Cursor);
Table1.Close;
(Table1 as TDBDataSet).Handle := Cursor;
end;
Si le composant ensemble de données était utilisé auparavant pour représenter
un autre ensemble de résultats SQL, appelez sa méthode Close pour désactiver
correctement ce curseur de données avant d’affecter le nouveau au composant
ensemble de données.
L’ensemble de résultats renvoyé via le paramètre Cursor de la méthode Execute
est toujours en lecture seule.

Exécution d’instructions SQL paramétrées


Certaines instructions SQL contiennent des paramètres par l’intermédiaire
desquels des valeurs sont transmises.
Pour exécuter ces instructions contenant des paramètres, appelez la méthode
TDatabase.Execute et passez un objet du type TParams dans le paramètre Params.
L’instruction SQL à exécuter est représentée par une valeur String (variable ou
littérale) et utilisée comme paramètre SQL de la méthode Execute. Si l’instruction
ne renvoie pas d’ensemble de résultats, passez la valeur nil comme paramètre
Cursor. Si l’instruction renvoie un ensemble de résultats, voir “Exécution
d’instructions SQL avec ensembles de résultats” à la page 17-12.
Pour représenter les paramètres, utilisez un objet du type TParams. Passez cet
objet TParams comme paramètre Params de la méthode Execute. Chaque
paramètre de l’instruction SQL est représenté dans l’objet TParams par un objet

Connexion aux bases de données 17-13


Exécution d’instructions SQL depuis un composant TDatabase

TParam distinct. Utilisez la méthode TParams.CreateParam pour ajouter à l’objet


TParams un objet TParam pour chaque paramètre de l’instruction SQL. Utilisez
les propriétés et les méthodes de TParam pour définir la valeur de l’objet TParam
et donner une valeur au paramètre SQL lorsque la méthode Execute est appelée.
Dans l’exemple suivant, une instruction SQL utilisant des paramètres est exécutée.
L’instruction SQL contient un seul paramètre nommé StateParam. Un objet
TParams (nommé stmtParams) est créé à l’intérieur de la routine et la méthode
TParams.CreateParam est appelée pour ajouter un seul objet TParam à stmtParams.
Une fois l’objet TParam créé, la valeur “CA” lui est affectée . Puis, la méthode
Execute est appelée en utilisant l’objet stmtParams comme paramètre Params.
procedure TDataForm.SELECT_WithParamsButtonClick(Sender: TObject);
var
SQLstmt: String;
stmtParams: TParams;
Cursor: hDBICur;
begin
stmtParams := TParams.Create;
try
Database1.Connected := True;
stmtParams.CreateParam(ftString, 'StateParam', ptInput);
stmtParams.ParamByName('StateParam').AsString := 'CA';
SQLstmt := 'SELECT Company, State '+
'FROM "Customer.db" ' +
'WHERE (State = :StateParam) ' +
'ORDER BY State, Company';
Database1.Execute(SQLstmt, stmtParams, False, @Cursor);
Table1.Close;
TDBDataSet(Table1).Handle := Cursor;
finally
stmtParams.Free;
end;
end;
Pour que l’exécution d’une instruction SQL paramétrée à l’aide de la méthode
Execute réussisse, il faut que les conditions suivantes soient remplies :
1 Les paramètres doivent se trouver dans l’instruction SQL (éléments précédés
de deux-points).
2 Un objet TParams doit être créé pour contenir les objets TParam.
3 Un objet TParam doit être créé dans l’objet TParams pour chacun des
paramètres de l’instruction SQL.
4 L’objet TParams doit être utilisé comme paramètre Params de la méthode
Execute.
Si un paramètre se trouve dans l’instruction SQL alors qu’aucun objet TParam n’a
été créé pour le représenté, le paramètre de l’instruction SQL ne peut pas
recevoir de valeur et l’instruction SQL peut produire une erreur lors de son
exécution (cela dépend du type de la base de données utilisée). Si un objet
TParam est fourni alors qu’aucun paramètre ne lui correspond dans l’instruction
SQL, une exception est provoquée lorsque l’application tente d’utiliser le TParam.

17-14 Guide du développeur


Chapitre

Présentation des ensembles


Chapter 18
18
de données
Dans Delphi, l’unité fondamentale pour accéder aux données est la famille
d’objets ensemble de données. Les applications utilisent des ensembles de données
pour tous les accès aux bases de données. En principe, un objet ensemble de
données représente une table particulière d’une base de données ou bien une
requête ou une procédure stockée permettant d’accéder à la base de données.
Tous les objets ensemble de données utilisés dans des applications de bases de
données descendent du même objet virtuel, TDataSet. Leurs champs de données,
leurs propriétés, méthodes et événements sont tous hérités de TDataSet. Ce
chapitre décrit les fonctionnalités de TDataSet héritées par les objets ensemble de
données utilisés dans les applications de bases de données. Avant d’utiliser tout
objet de base de données, vous devez avoir assimilé ces fonctionnalités partagées.

Présentation des ensembles de données 18-1


Présentation de l’objet TDataSet

La figure 18.1 illustre les relations hiérarchiques existant entre les composants
ensemble de données :
Figure 18.1 Hiérarchie des composants ensemble de données sous Delphi

TDataSet

TCustomADODataSet TBDEDataSet TClientDataSet

TADOCommand TNestedTable

TADODataSet TDBDataSet

TADOQuery TTable

TADOStoredProc TQuery

TStoredProc

Présentation de l’objet TDataSet


TDataSet est l’ancêtre de tous les objets ensemble de données utilisés dans les
applications. Il définit un ensemble de champs de données, de propriétés,
d’événements et de méthodes partagés par tous les objets ensembles de données.
TDataSet est un ensemble de données virtuel, ce qui signifie que la plupart de
ses propriétés et méthodes sont déclarées comme virtual ou abstract. Une
méthode virtuelle est une déclaration de fonction ou de procédure dont
l’implémentation est redéfinie par les objets descendants. Une méthode abstract est
une fonction ou une procédure sans véritable implémentation. La déclaration est
un prototype qui décrit une méthode (ainsi que ses paramètres et le type
renvoyé, le cas échéant) devant être implémentée dans tous les objets ensemble
de données descendants (l’implémentation peut être différente pour chaque
ensemble de données).
Etant donné que TDataSet contient des méthodes abstract, vous ne pouvez pas
l’utiliser directement dans une application sans générer d’erreur d’exécution.
Vous devez plutôt créer des instances des descendants de TDataSet (TTable,
TQuery, TStoredProc et TClientDataSet ) et les utiliser dans vos applications. Vous
avez aussi la possibilité de dériver des objets ensemble de données de TDataSet
ou de ses descendants et d’écrire des implémentations pour toutes ses méthodes
abstract.

18-2 Guide du développeur


Types d’ensembles de données

Toutefois, TDataSet définit la plupart des fonctionnalités communes à tous les


objets ensemble de données. Ainsi, il définit la structure de base de tous les
ensembles de données : un tableau de composants TField qui correspond aux
colonnes d’une ou de plusieurs tables de base de données, aux champs de référence
ou aux champs calculés fournis par votre application. Pour plus d’informations sur
les composants TField, voir chapitre 19, “Manipulation des composants champ”
Les points suivants sont abordés dans ce chapitre :
• Types d’ensembles de données
• Ouverture et fermeture des ensembles de données
• Détermination et définition des états d’un ensemble de données
• Navigation dans les ensembles de données
• Recherche dans les ensembles de données
• Affichage et édition d’ensembles de données en utilisant des filtres
• Modification des données
• Utilisation des événements des ensembles de données
• Utilisation des ensembles de données orientés BDE

Types d’ensembles de données


Pour comprendre les concepts communs à tous les objets ensemble de données et
vous préparer au développement d’objets ensemble de données personnalisés
non basés sur le BDE (moteur de bases de données Borland) ni sur les ADO
(ActiveX Data Objects), lisez ce chapitre.
Pour développer des applications de bases de données traditionnelles client/
serveur à niveau double à l’aide du moteur de bases de données Borland (BDE),
voir “Présentation de l’orientation BDE,” plus loin dans ce chapitre. Cette
section présente TBDEDataSet et TDBDataSet, et met l’accent sur les
caractéristiques communes de TQuery, TStoredProc et TTable, qui sont les
composants ensemble de données les plus couramment utilisés dans les
applications de bases de données.
Avec certaines versions de Delphi, vous pouvez développer des applications de
bases de données multiniveaux à l’aide d’ensembles de données distribués. Pour
plus d’informations sur la manipulation des ensembles de données client dans les
applications multiniveaux, voir chapitre 14, “Création d’applications
multiniveaux”. Ce chapitre explique comment utiliser TClientDataSet et connecter
le client à un serveur d’applications.

Ouverture et fermeture des ensembles de données


Pour lire ou écrire des données dans une table ou par l’intermédiaire d’une
requête, une application doit d’abord ouvrir un ensemble de données. Il y a
deux moyens de procéder :
• Définir la propriété Active de l’ensemble de données à True, soit lors de la
conception, depuis l’inspecteur d’objets, ou à l’exécution depuis le code :
CustTable.Active := True;

Présentation des ensembles de données 18-3


Détermination et définition des états d’un ensemble de données

• Appeler la méthode Open de l’ensemble de données au moment de l’exécution :


CustQuery.Open;
Il y a deux façons de fermer un ensemble de données :
• Définir la propriété Active de l’ensemble de données à False, soit lors de la
conception, depuis l’inspecteur d’objets, ou à l’exécution depuis le code :
CustQuery.Active := False;
• Appeler la méthode Close de l’ensemble de données à l’exécution :
CustTable.Close;
Pour changer certaines propriétés de l’ensemble de données, il est parfois
nécessaire de le fermer au préalable. C’est le cas, par exemple, de la propriété
TableName ou des composants TTable. Lors de l’exécution, vous pouvez fermer
l’ensemble de données pour des raisons spécifiques à votre application.

Détermination et définition des états d’un ensemble de données


L’état, ou le mode, d’un ensemble de données détermine les opérations possibles
sur ses données. Par exemple, quand un ensemble de données est fermé, son état
devient dsInactive, ce qui signifie que rien ne peut affecter ses données. Un
ensemble de données est toujours dans un état déterminé. Au moment de
l’exécution, vous pouvez examiner la propriété State de l’ensemble de données,
accessible en lecture seulement, pour déterminer son état en cours. Le tableau
suivant récapitule les valeurs possibles de la propriété State et leur signification :

Tableau 18.1 Valeurs possibles pour la propriété State des ensembles de données
Valeur Etat Signification
dsInactive Inactif L’ensemble de données est fermé. Ses données sont
indisponibles.
dsBrowse Visualisation L’ensemble de données est ouvert. Ses données sont
visualisables mais ne peuvent être modifiées. C’est l’état par
défaut d’un ensemble de données ouvert.
dsEdit Edition L’ensemble de données est ouvert. La ligne en cours peut
être modifiée.
dsInsert Insertion L’ensemble de données est ouvert. Une nouvelle ligne peut
être insérée.
dsSetKey Indexation S’applique à TTable et TClientDataSet uniquement. L’ensemble
(SetKey) de données est ouvert. Active la définition de portées et de
valeurs clé pour les opérations portant sur des portées et les
opérations GotoKey.
dsCalcFields Champs L’ensemble de données est ouvert. Indique qu’un événement
calculés OnCalcFields est en cours. Interdit toute modification de
(CalcFields) champ non calculé.
dsCurValue CurValue Utilisation interne uniquement.
dsNewValue NewValue Utilisation interne uniquement.

18-4 Guide du développeur


Détermination et définition des états d’un ensemble de données

Tableau 18.1 Valeurs possibles pour la propriété State des ensembles de données
Valeur Etat Signification
dsOldValue OldValue Utilisation interne uniquement.
dsFilter Filtrage L’ensemble de données est ouvert. Indique qu’une opération
de filtrage est en cours. Un ensemble de données restreint
peut être visualisé, sans qu’aucune donnée ne puisse être
changée.

Quand une application ouvre un ensemble de données, elle bascule


automatiquement en mode dsBrowse. L’état d’un ensemble de données évolue au
fur et à mesure que l’application traite les données. Un ensemble de données
ouvert bascule d’un état à un autre en fonction des points suivants :
• Le code de votre application.
• Un comportement prédéterminé des composants orientés données.
Pour basculer un ensemble de données dans les états dsBrowse, dsEdit, dsInsert ou
dsSetKey, il suffit d’appeler la méthode correspondant au nom de l’état voulu.
Par exemple, le code suivant bascule CustTable dans l’état dsInsert, accepte le
nouvel enregistrement saisi par l’utilisateur, puis écrit le nouvel enregistrement
dans la base de données :
CustTable.Insert; { L’application met explicitement l’ensemble de données à l’état
Insertion }
AddressPromptDialog.ShowModal;
if AddressPromptDialog.ModalResult := mrOK then
CustTable.Post; { Delphi bascule l’ensemble de données à l’état Visualisation
// si l’opération réussit }
else
CustTable.Cancel; {Delphi bascule l’ensemble de données à l’état Visualisation
// en cas d’annulation }
Cet exemple illustre aussi le chamgement automatique d’état d’un ensemble de
données à dsBrowse quand :
• La méthode Post écrit avec succès un enregistrement dans la base de données.
(Si Post échoue, l’état de l’ensemble de données reste inchangé.)
• La méthode Cancel est appelée.
Certains états ne peuvent être directement définis. Par exemple, pour mettre un
ensemble de données à l’état dsInactive, vous devez basculer sa propriété Active à
False, ou bien appeler sa méthode Close. Les instructions suivantes sont
équivalentes :
CustTable.Active := False;
CustTable.Close;
Les autres états (dsCalcFields, dsCurValue, dsNewValue, dsOldValue et dsFilter) ne
peuvent être définis par votre application. Par contre, ils sont définit en fonction
des besoins. Par exemple, dsCalcFields est défini quand l’événement OnCalcFields
d’un ensemble de données est appelé. Lorsque OnCalcFields achève son
exécution, l’ensemble de données revient à son état précédent.

Présentation des ensembles de données 18-5


Détermination et définition des états d’un ensemble de données

Remarque A chaque fois que l’état d’un ensemble de données change, l’événement
OnStateChange est appelé pour tous les composants source de données associés.
Pour plus d’informations concernant les composants source de données et sur
OnStateChange, voir “Utilisation des sources de données” à la page 26-6.
Les sections suivantes présentent chacun de ces états en indiquant comment ils
sont définis, leurs relations et l’endroit où chercher d’éventuelles informations
complémentaires.

Désactivation d’un ensemble de données


Un ensemble de données est inactif quand il est fermé. L’accès aux
enregistrements est alors interdit. Au moment de la conception, un ensemble de
données reste fermé tant que sa propriété Active n’est pas mise à True. A
l’exécution, un ensemble de données reste fermé tant qu’il n’est pas ouvert par
un appel à sa méthode Open, ou par la bascule de sa propriété Active à True.
Lorsque vous ouvrez un ensemble de données inactif, il est mis
automatiquement à l’état dsBrowse. Le diagramme suivant illustre les relations
entre ces états et les méthodes qui les définissent.
Figure 18.2 Relations entre les états Inactif et Visualisation
Close
Inactif Visualisation
Open

Pour rendre un ensemble de données inactif, il suffit d’appeler sa méthode Close.


Vous pouvez écrire les gestionnaires d’événements BeforeClose et AfterClose pour
répondre à la méthode Close de l’ensemble de données. Par exemple, si un ensemble
de données est dans le mode dsEdit ou dsInsert quand l’application appelle Close,
vous devez demander s’il souhaite émettre ou annuler les modifications en suspens
avant sa fermeture. Le code suivant illustre un tel gestionnaire :
procedure CustTable.VerifyBeforeClose(DataSet: TDataSet)
begin
if (CustTable.State = dsEdit) or (CustTable.State = dsInsert) then
begin
if MessageDlg('Post changes before closing?', mtConfirmation, mbYesNo, 0) = mrYes then
CustTable.Post;
else
CustTable.Cancel;
end;
end;
Pour associer une procédure à l’événement BeforeClose d’un ensemble de données
au moment de la conception, accomplissez les étapes suivantes :
1 Sélectionnez la table dans le module de données (ou dans la fiche).
2 Cliquez sur la page Evénements dans l’inspecteur d’objets.
3 Saisissez le nom de la procédure de l’événement BeforeClose (ou choisissez-le
dans la liste déroulante).

18-6 Guide du développeur


Détermination et définition des états d’un ensemble de données

Visualisation d’un ensemble de données


Quand une application ouvre un ensemble de données, il passe automatiquement
à l’état dsBrowse. Cet état vous permet de voir les enregistrements d’un ensemble
de données, sans les modifier ni insérer de nouveaux enregistrements. dsBrowse
sert principalement à passer d’un enregistrement à un autre dans un ensemble
de données. Pour plus d’informations concernant le défilement d’enregistrements,
voir “Navigation dans les ensembles de données” à la page 18-10.
Tous les autres états sont accessibles à partir de dsBrowse. Par exemple, le fait
d’appeler la méthode Insert ou Append d’un ensemble de données le fait passer
de l’état dsBrowse à dsInsert (notez que d’autres facteurs ou d’autres propriétés de
l’ensemble de données, comme CanModify, peuvent empêcher ce changement).
L’appel à SetKey pour rechercher des enregistrements a pour effet de mettre
l’ensemble de données en mode dsSetKey. Pour plus d’informations concernant
l’insertion et l’ajout d’enregistrements dans un ensemble de données, voir
“Modification des données” à la page 18-24.
Deux méthodes, associées à tous les ensembles de données, provoquent le retour
à l’état dsBrowse de ces ensembles. Cancel met fin à la tâche d’édition, d’insertion
ou de recherche en cours et renvoie systématiquement l’ensemble de données à
l’état dsBrowse. Post tente d’écrire les modifications dans la base de données et, si
l’opération réussit, renvoie aussi l’ensemble de données à l’état dsBrowse. SiPost
échoue, l’état en cours reste inchangé.
Le diagramme suivant illustre les relations entre dsBrowse et les autres modes
que vous pouvez définir pour les ensembles de données dans votre application
ainsi que les méthodes qui définissent ces modes.
Figure 18.3 Relations entre l’état Visualisation et les autres états d’un ensemble de données

dsInactive

Open Close
Insert
Append Edit
dsInsert dsBrowse dsEdit

Post Post (réussite) Post (réussite) Post


(échec) Cancel Cancel (échec)
Delete Delete

SetKey, EditKey Post, Cancel,


SetRange GotoKey, FindKey
ApplyRange, CancelRange

dsSetKey

Présentation des ensembles de données 18-7


Détermination et définition des états d’un ensemble de données

Activation de l’édition d’un ensemble de données


Un ensemble de données doit se trouver en mode dsEdit pour que l’application
puisse modifier les enregistrements. Vous pourrez utiliser la méthode Edit pour
basculer un ensemble de données en mode dsEdit si la propriété CanModify
(accessible en lecture seulement) de l’ensemble de données est à True. Ce qui est
le cas si la base de données sous-jacente à l’ensemble de données reconnaît les
droits d’accès en lecture et en écriture.
Certains contrôles orientés données de vos fiches peuvent mettre un ensemble de
données à l’état dsEdit si :
• La propriété ReadOnly du contrôle est à False (la valeur par défaut).
• La propriété AutoEdit de la source de données du contrôle est à True.
• CanModify est à True pour l’ensemble de données.
Important Lorsque la propriété ReadOnly des composants TTable est à True, CanModify est à
False, afin d’empêcher la modification des enregistrements. Le même effet est
obtenu lorsque la propriété RequestLive des composants TQuery est à False.
Remarque Même si un ensemble de données est à l’état dsEdit, la modification
d’enregistrements peut ne pas réussir dans le cas de bases de données SQL, si
l’utilisateur de votre application ne dispose pas des droits d’accès SQL
appropriés.
Dans le code, un ensemble de données peut passer de l’état dsEdit à l’état
dsBrowse en appelant la méthode Cancel, Post ou Delete. Cancel annule les
modifications du champ ou de l’enregistrement en cours. Post tente d’écrire
l’enregistrement modifié dans l’ensemble de données, et si elle réussit, elle
renvoie l’ensemble de données à l’état dsBrowse. Si Post ne peut écrire ces
changements, l’ensemble de données reste à l’état dsEdit. Delete tente de
supprimer l’enregistrement en cours dans l’ensemble de données, et si elle
réussit, renvoie l’ensemble de données à l’état dsBrowse. Si Delete échoue,
l’ensemble de données reste à l’état dsEdit.
Les contrôles orientés données, pour lesquels la modification est
automatiquement activée, appellent Post quand l’utilisateur exécute une action
qui modifie la position du curseur (comme le déplacement vers un autre
enregistrement dans une grille) ou qui provoque la perte de la focalisation par le
contrôle (comme le déplacement vers un autre contrôle de la fiche).
Pour en savoir plus sur la modification des champs et des enregistrements dans
un ensemble de données, voir “Modification des données” à la page 18-24.

Activation de l’insertion de nouveaux enregistrements


Un ensemble de données doit se trouver en mode dsInsert pour qu’une
application puisse ajouter de nouveaux enregistrements. Vous pourrez utiliser la
méthode Insert ou Append pour basculer un ensemble de données en mode
dsInsert si la propriété CanModify (accessible en lecture seulement) de l’ensemble
de données est à True. C’est le cas si la base de données sous-jacente à
l’ensemble de données reconnaît les droits d’accès en lecture et en écriture.

18-8 Guide du développeur


Détermination et définition des états d’un ensemble de données

Dans les fiches de votre application, les contrôles orientés données grille et
navigateur peuvent mettre un ensemble de données à l’état dsInsert si :
• La propriété ReadOnly du contrôle est à False (la valeur par défaut).
• La propriété AutoEdit de la source de données du contrôle est à True.
• CanModify est à True pour l’ensemble de données.
Important Si la propriété ReadOnly des composants TTable est à True, CanModify est à False,
afin d’empêcher la modification des enregistrements. De même, pour les
composants TQuery, si la propriété RequestLive est à False, CanModify a la valeur
False.
Remarque Même si un ensemble de données est à l’état dsInsert, l’insertion d’enregistrements
peut ne pas réussir dans le cas de bases de données SQL, si l’utilisateur de votre
application ne dispose pas des droits d’accès SQL appropriés.
Dans votre code, vous pouvez renvoyer un ensemble de données de l’état
dsInsert à l’état dsBrowse en appelant l’une des méthodes Cancel, Post ou Delete.
Delete et Cancel annulent le nouvel enregistrement. Post tente d’écrire le nouvel
enregistrement dans l’ensemble de données, et si elle réussit, renvoie l’ensemble
de données à l’état dsBrowse. Si Post ne peut écrire l’enregistrement, l’ensemble
de données reste à l’état dsInsert.
Les contrôles orientés données, pour lesquels l’insertion est automatiquement
activée, appellent Post quand l’utilisateur exécute une action qui change la position
du curseur (comme le déplacement vers un autre enregistrement dans une grille).
Pour en savoir plus sur l’insertion et l’ajout d’enregistrements dans un ensemble
de données, voir “Modification des données” à la page 18-24.

Activation de recherches indexées et définition de portées


Vous pouvez rechercher des enregistrements spécifiques dans un ensemble de
données en utilisant les méthodes génériques de recherche Locate et Lookup.
Toutefois, les composants TTable disposent de méthodes supplémentaires, GotoKey
et FindKey, qui vous permettent de rechercher des enregistrements à partir de
l’index d’une table. Ces méthodes peuvent être utilisées sur un composant table s’il
est à l’état dsSetKey. Le mode dsSetKey ne s’applique qu’aux composants TTable.
Pour mettre un ensemble de données en mode dsSetKey, vous devez utiliser la
méthode SetKey à l’exécution. Les méthodes GotoKey, GotoNearest, FindKey et
FindNearest remettent l’ensemble de données à l’état dsBrowse à la fin de la
recherche. Pour plus d’informations sur les recherches indexées dans une table,
voir “Recherche d’enregistrements à partir des champs indexés” à la page 20-6.
Il est possible de visualiser temporairement et d’éditer un sous-ensemble de
données en utilisant des filtres. Pour plus d’informations sur les filtres, voir
“Affichage et édition d’ensembles de données en utilisant des filtres” à la
page 18-19. Les composants TTable supportent aussi les portées (un moyen
supplémentaire pour accéder à un sous-ensemble d’enregistrements). Pour créer
ou appliquer une portée sur une table, cette dernière doit être en mode dsSetKey.
Pour plus d’informations sur la définition de portées, voir “Manipulation d’un
sous-ensemble de données” à la page 20-12.

Présentation des ensembles de données 18-9


Navigation dans les ensembles de données

Champs calculés
Delphi bascule un ensemble de données en mode dsCalcFields à chaque fois qu’une
application appelle le gestionnaire d’événement OnCalcFields de l’ensemble de
données. Cet état empêche les modifications et les ajouts dans les enregistrements
de l’ensemble de données sauf s’ils s’appliquent aux champs calculés modifiés par
le gestionnaire lui-même. La raison en est que OnCalcFields utilise les valeurs des
autres champs pour dériver celles des champs calculés. Sinon, les changements
des autres champs pourraient invalider les valeurs affectées aux champs calculés.
Quand ce gestionnaire achève son exécution, l’ensemble de données revient à
l’état dsBrowse.
Pour plus d’informations concernant la création de champs calculés et le
gestionnaire OnCalcFields, voir “Utilisation de l’événement OnCalcFields” à la
page 18-30.

Filtrage d’enregistrements
Delphi met un ensemble de données en mode dsFilter à chaque fois qu’une
application appelle le gestionnaire d’événement OnFilterRecord de l’ensemble de
données. Cet état empêche toute modification ou ajout dans les enregistrements
de l’ensemble de données durant le processus de filtrage afin de ne pas
l’invalider. Pour plus d’informations sur le filtrage, voir “Affichage et édition
d’ensembles de données en utilisant des filtres” à la page 18-19.
Quand le gestionnaire OnFilterRecord achève son exécution, l’ensemble de
données revient à l’état dsBrowse.

Mise à jour d’enregistrements


Lors de la réalisation d’opérations de mise à jour en mémoire cache, Delphi peut
placer temporairement l’ensemble de données dans l’un des états dsNewValue,
dsOldValue, ou dsCurValue. Ces états indiquent généralement qu’un gestionnaire
d’événement OnUpdateError accède aux propriétés correspondantes d’un
composant champ (respectivement NewValue, OldValue et CurValue). Vos
applications ne peuvent pas voir ni définir ces états.

Navigation dans les ensembles de données


Chaque ensemble de données actif dispose d’un curseur qui pointe sur la ligne
en cours dans l’ensemble de données. Ce sont les valeurs de cette ligne en cours
qui sont manipulables par les méthodes d’édition, d’insertion et de suppression.
Les valeurs de champ apparaissant dans les contrôles à champ unique, orientés
données d’une fiche (comme TDBEdit, TDBLabel et TDBMemo) proviennent aussi
de cette ligne.

18-10 Guide du développeur


Navigation dans les ensembles de données

Vous pouvez changer de ligne en cours en déplaçant le curseur pour le faire


pointer sur une autre ligne. Le tableau suivant dresse la liste des méthodes
pouvant être utilisées dans le code d’une application pour se déplacer vers
d’autres enregistrements.

Tableau 18.2 Méthodes de navigation relatives aux ensembles de données


Méthode Description
First Déplace le curseur sur la première ligne d’un ensemble de données.
Last Déplace le curseur sur la dernière ligne d’un ensemble de données.
Next Déplace le curseur sur la ligne suivante dans un ensemble de données.
Prior Déplace le curseur sur la ligne précédente dans un ensemble de données.
MoveBy Déplace le curseur d’un nombre de lignes spécifié vers l’avant ou vers
l’arrière dans un ensemble de données.

Le composant visuel, orienté données, TDBNavigator encapsule ces méthodes sous


la forme de boutons sur lesquels l’utilisateur peut cliquer pour se déplacer parmi
les enregistrements lors de l’exécution. Pour plus d’informations concernant le
composant navigateur, voir chapitre 26, “Utilisation de contrôles de données”
Outre ces méthodes, deux propriétés booléennes relatives aux ensembles de
données donnent des indications utiles pour parcourir les enregistrements d’un
ensemble de données.

Tableau 18.3 Propriétés de navigation des ensembles de données


Propriété Description
Bof (Début de fichier) True: le curseur se trouve sur la première ligne de l’ensemble de
données.
false: le curseur n’est pas répertorié comme étant sur la première
ligne de l’ensemble de données.
Eof (Fin de fichier) True: le curseur se trouve sur la dernière ligne de l’ensemble de
données.
false: le curseur n’est pas répertorié comme étant sur la dernière ligne
de l’ensemble de données.

Utilisation des méthodes First et Last


La méthode First déplace le curseur sur la première ligne d’un ensemble de
données et bascule la propriété Bof à True. Si le curseur est déjà sur la première
ligne, First n’a aucun effet.
Par exemple, le code suivant se déplace sur le premier enregistrement de
CustTable :
CustTable.First;
La méthode Last déplace le curseur sur la dernière ligne d’un ensemble de
données et bascule la propriété Eof à True. Si le curseur est déjà sur la dernière
ligne, Last n’a aucun effet.

Présentation des ensembles de données 18-11


Navigation dans les ensembles de données

Le code suivant se déplace sur le dernier enregistrement de CustTable :


CustTable.Last;
Remarque Bien qu’il puisse exister de nombreuses raisons pour se déplacer sur la première
ou la dernière ligne d’un ensemble de données sans que l’utilisateur
n’intervienne, vous devez offrir à ce dernier la possibilité de naviguer parmi les
enregistrements en utilisant un composant TDBNavigator. Le composant
navigateur contient des boutons qui, s’ils sont actifs et visibles, permettent à
l’utilisateur d’aller sur la première ou la dernière ligne de l’ensemble de données
actif. Les événements OnClick de ces boutons appellent les méthodes First et Last
de l’ensemble de données. Pour plus d’informations concernant le
fonctionnement du composant navigateur, voir chapitre 26, “Utilisation de
contrôles de données”.

Utilisation des méthodes Next et Prior


La méthode Next déplace le curseur d’une ligne vers l’avant dans un ensemble
de données non vide en mettant la propriété Bof à False. Si, lorsque vous appelez
Next, le curseur se trouve déjà sur la dernière ligne dans l’ensemble de données,
Next n’a aucun effet.
Par exemple, le code suivant provoque le déplacement du curseur sur le
prochain enregistrement de CustTable :
CustTable.Next;
La méthode Prior déplace le curseur d’une ligne vers l’arrière dans l’ensemble de
données non vide en mettant la propriété Eof à False. Si, lorsque vous appelez
Prior, le curseur se trouve déjà sur la première ligne dans l’ensemble de données,
Prior n’a aucun effet.
Par exemple, le code suivant se déplace sur l’enregistrement précédent de
CustTable :
CustTable.Prior;

Utilisation de la méthode MoveBy


MoveBy vous permet de spécifier le nombre de lignes du déplacement du curseur
dans l’ensemble de données, vers l’avant ou vers l’arrière. Le déplacement se fait
par rapport à la position de l’enregistrement en cours au moment où MoveBy a été
appelée. En outre, MoveBy définit les propriétés Bof et Eof de l’ensemble de données.
Cette fonction accepte un paramètre entier qui indique le nombre
d’enregistrements parcourus lors du déplacement. Un entier positif indique un
déplacement vers l’avant et un entier négatif, un déplacement vers l’arrière.
MoveBy renvoie le nombre de lignes effectivement parcourues. Si le déplacement
voulu va au-delà du début ou de la fin de l’ensemble de données, le nombre de
lignes renvoyées par MoveBy sera différent de celui voulu pour le déplacement.
Ceci parce que MoveBy s’arrête quand il atteint le premier ou le dernier
enregistrement de l’ensemble de données.

18-12 Guide du développeur


Navigation dans les ensembles de données

Le code suivant provoque un déplacement de deux enregistrements vers l’arrière


dans CustTable :
CustTable.MoveBy(-2);
Remarque Si vous utilisez MoveBy dans votre application alors que vous travaillez dans un
environnement de bases de données multi-utilisateur, vous devez garder présent
à l’esprit que les ensembles de données sont des entités fluides. Un
enregistrement qui, il y a peu, se situait cinq enregistrements vers l’arrière, peut
maintenant se trouver quatre, six, voire même un nombre indéterminé
d’enregistrements vers l’arrière. Ceci est dû au fait que plusieurs utilisateurs
peuvent accéder simultanément à la base de données en changeant les données.

Utilisation des propriétés Eof et Bof


Les deux propriétés Bof (début de fichier) et Eof (fin de fichier), accessibles en
lecture et à l’exécution uniquement, sont utiles pour contrôler la navigation dans
un ensemble de données et tout particulièrement pour parcourir tous ses
enregistrements.

Eof
La valeur True de Eof indique que le curseur se trouve sans équivoque sur la
dernière ligne de l’ensemble de données. EOF passe à True quand une
application :
• Ouvre un ensemble de données vide.
• Appelle la méthode Last de l’ensemble de données.
• Appelle la méthode Next de l’ensemble de données et que son exécution
échoue (car le curseur se trouve déjà sur la dernière ligne de l’ensemble de
données).
• Appelle SetRange sur une portée ou un ensemble de données vide.
Eof vaut False dans tout autre cas ; vous devez supposer que Eof vaut False sauf
si l’une des conditions ci-dessus est vérifiée et que vous avez testé directement la
valeur de la propriété.
Le test sur Eof se fait généralement dans une condition de boucle pour contrôler
le processus itératif sur des enregistrements d’un ensemble de données. Eof vaut
False si vous ouvrez un ensemble de données contenant plusieurs
enregistrements (ou si vous appelez First). Pour parcourir un à un les
enregistrements d’un ensemble de données, vous devez créer une boucle qui se
termine quand Eof vaut True. A l’intérieur de la boucle, vous devez appeler Next
pour chaque enregistrement de l’ensemble de données. Eof reste à True jusqu’à ce
que Next soit appelée alors que le curseur se trouve déjà sur le dernier
enregistrement.

Présentation des ensembles de données 18-13


Navigation dans les ensembles de données

L’exemple suivant montre l’une des façons de programmer une boucle de


traitement d’enregistrements pour un ensemble de données appelé CustTable :
CustTable.DisableControls;
try
CustTable.First; { Go to first record, which sets EOF False }
while not CustTable.EOF do { Cycle until EOF is True }
begin
{ Process each record here }
ƒ
CustTable.Next; { EOF False on success; EOF True when Next fails on last record }
end;
finally
CustTable.EnableControls;
end;
Astuce Cet exemple montre aussi comment désactiver puis réactiver des contrôles
visuels, orientés données, rattachés à l’ensemble de données. Si vous désactivez
les contrôles visuels pendant la durée de l’itération sur l’ensemble de données, le
traitement sera accéléré car Delphi n’a pas à mettre à jour le contenu des
contrôles au fur et à mesure de l’évolution de l’enregistrement en cours. Une fois
l’itération achevée, les contrôles doivent être réactivés pour être mis à jour en
fonction de la nouvelle ligne en cours. Notez que l’activation de contrôles visuels
a lieu dans la clause finally d’une instruction try...finally. Ceci permet d’être
certain que les contrôles ne resteront pas désactivés, même si une exception
termine le traitement de la boucle de façon prématurée.

Bof
La valeur True de la propriété Bof indique que le curseur se trouve sans
équivoque sur la première ligne de l’ensemble de données. Bof est mis à True
lorsqu’une application :
• Ouvre l’ensemble de données.
• Appelle la méthode First de l’ensemble de données.
• Appelle la méthode Prior de l’ensemble de données et que son exécution
échoue (car le curseur se trouve déjà sur la première ligne de l’ensemble de
données).
• Appelle SetRange sur une portée ou un ensemble de données vide.
Bof vaut False dans tout autre cas ; vous devez supposer que Bof vaut False sauf
si l’une des conditions ci-dessus est vérifiée et que vous avez testé directement la
valeur de la propriété.
Comme Eof, Bof peut se trouver dans une condition de boucle pour contrôler un
processus itératif sur des enregistrements d’un ensemble de données. L’exemple
suivant montre l’une des façons de programmer une boucle de traitement
d’enregistrements pour un ensemble de données appelé CustTable:
CustTable.DisableControls; { Speed up processing; prevent screen flicker }
try
while not CustTable.BOF do { Cycle until BOF is True }

18-14 Guide du développeur


Navigation dans les ensembles de données

begin
{ Process each record here }
ƒ
CustTable.Prior; { BOF False on success; BOF True when Prior fails on first record }
end;
finally
CustTable.EnableControls; { Display new current row in controls }
end;

Marquage d’enregistrements
Outre la possibilité de se déplacer d’un enregistrement à un autre dans un
ensemble de données (ou de se déplacer selon un nombre déterminé
d’enregistrements), il est possible de marquer un emplacement particulier dans
un ensemble de données de façon à y revenir rapidement le moment voulu.
TDataSet et ses descendants implémentent une fonction de définition de signets
qui permet de repérer un enregistrement dans un ensemble de données pour
pouvoir y revenir ultérieurement. Cette fonction se présente sous la forme de la
propriété Bookmark et de cinq méthodes de définition de signets.
La propriété Bookmark indique quel est le signet en cours dans votre application.
Bookmark est une chaîne qui identifie le signet en cours. Tout nouveau signet
ajouté devient le signet en cours.
TDataSet implémente des méthodes virtuelles de gestion des signets. Alors que
ces méthodes garantissent que chaque objet ensemble de données dérivé de
TDataSet renvoie une valeur si une méthode de signet est appelée, les valeurs
renvoyées sont simplement des valeurs par défaut qui n’indiquent pas la
position en cours. Les descendants de TDataSet, comme TBDEDataSet,
réimplémentent les méthodes de signets de façon à ce qu’elles renvoient des
valeurs significatives :
• BookmarkValid permet de déterminer si un signet donné est en cours
d’utilisation.
• CompareBookmarks est utilisée pour tester deux signets et vérifier s’ils sont
identiques.
• GetBookmark alloue un signet en l’associant à la position en cours dans
l’ensemble de données.
• GotoBookmark se positionne sur un signet précédemment créé par GetBookmark
• FreeBookmark restitue un signet qui a été alloué par GetBookmark.
Pour créer un signet, déclarez une variable de type TBookmark dans votre
application, puis appelez GetBookmark pour allouer l’espace de stockage associé à
la variable et définir sa valeur par un emplacement particulier dans l’ensemble
de données. La variable TBookmark est un pointeur (void *).

Présentation des ensembles de données 18-15


Navigation dans les ensembles de données

Avant d’appeler GotoBookmark pour accéder à un enregistrement donné, vous


pouvez appeler BookmarkValid pour déterminer si le signet pointe sur un
enregistrement. BookmarkValid renvoie True si le signet pointe sur un
enregistrement. Dans TDataSet, BookmarkValid est une méthode virtuelle qui
renvoie toujours False pour indiquer que le signet n’est pas correct. Les
descendants de TDataSet réimplémentent cette méthode pour renvoyer une
valeur significative.
CompareBookmarks peut être appelée pour voir si un signet sur lequel vous voulez
vous déplacer est différent d’un autre signet ou du signet en cours.
TDataSet.CompareBookmarks renvoie toujours 0 ; ce qui signifie que les signets ne
sont pas identiques. Les descendants de TDataSet réimplémentent cette méthode
pour renvoyer une valeur significative.
Lorsqu’un signet lui est transmis, GotoBookmark déplace le curseur de l’ensemble
de données à l’emplacement du signet. TDataSet.GotoBookmark appelle une
méthode interne virtuelle pure qui génère une erreur d’exécution. Les
descendants de TDataSet réimplémentent cette méthode pour renvoyer une
valeur significative.
FreeBookmark restitue la mémoire allouée à un signet lorsqu’il n’est plus
nécessaire. Vous devez également appeler FreeBookmark avant de réutiliser un
signet existant.
Le code suivant illustre l’utilisation des signets :
procedure DoSomething (const Tbl: TTable)
var
Bookmark: TBookmark;
begin
Bookmark := Tbl.GetBookmark; { Allocate memory and assign a value }
Tbl.DisableControls; { Turn off display of records in data controls }
try
Tbl.First; { Go to first record in table }
while not Tbl.EOF do {Iterate through each record in table }
begin
{ Do your processing here }
ƒ
Tbl.Next;
end;
finally
Tbl.GotoBookmark(Bookmark);
Tbl.EnableControls; { Turn on display of records in data controls, if necessary }
Tbl.FreeBookmark(Bookmark); {Deallocate memory for the bookmark }
end;
end;
Avant d’entrer dans le processus de parcours des enregistrements, les contrôles
sont désactivés. Même si une erreur se produit durant le balayage des
enregistrements, la clause finally permet d’être sûr que les contrôles seront
toujours réactivés et que le signet sera toujours restitué, même si la boucle se
termine prématurément.

18-16 Guide du développeur


Recherche dans les ensembles de données

Recherche dans les ensembles de données


Vous pouvez rechercher des enregistrements spécifiques dans un ensemble de
données en utilisant les méthodes génériques de recherche Locate et Lookup. Elles
autorisent des recherches dans tout type de colonne et dans tout ensemble de
données.

Utilisation de la méthode Locate


Locate déplace le curseur sur la première ligne correspondant au critère de
recherche spécifié. Dans sa forme la plus simple, vous transmettez à Locate le
nom de la colonne de recherche, une valeur de champ pour établir la
correspondance et un indicateur d’option qui spécifie si la recherche doit tenir
compte des différences majuscules/minuscules et si elle utilise les
correspondances de clés partielles. Par exemple, le code suivant déplace le
curseur sur la première ligne de CustTable pour laquelle la valeur dans la
colonne Company est “Professional Divers, Ltd.” :
var
LocateSuccess: Boolean;
SearchOptions: TLocateOptions;
begin
SearchOptions := [loPartialKey];
LocateSuccess := CustTable.Locate('Company', 'Professional Divers, Ltd.',
SearchOptions);
end;
Si Locate trouve une correspondance, le premier enregistrement contenant cette
correspondance devient l’enregistrement en cours. Locate renvoie True si une
correspondance est trouvée et False dans le cas contraire. Si la recherche échoue,
l’enregistrement en cours reste le même.
Le vrai potentiel de Locate se manifeste quand vous effectuez une recherche sur
plusieurs colonnes et que vous spécifiez plusieurs valeurs recherchées. Celles-ci
sont des variants, ce qui vous permet de spécifier des types de données différents
pour vos critères de recherche. Pour spécifier plusieurs colonnes dans une chaîne
de recherche, séparez les éléments de la chaîne par des points-virgules.
Les valeurs recherchées étant des variants, vous devez soit transmettre un type
tableau de variants comme argument (par exemple, les valeurs renvoyées par la
méthode Lookup), soit construire le tableau de variants à la volée en utilisant la
fonction VarArryOf pour transmettre plusieurs valeurs. Le code suivant illustre
une recherche sur plusieurs colonnes faisant intervenir de multiples valeurs de
recherche et utilisant les correspondances de clés partielles :
with CustTable do
Locate('Company;Contact;Phone', VarArrayOf(['Sight Diver','P']), loPartialKey);
Locate utilise la méthode de recherche la plus rapide pour trouver les
correspondances d’enregistrements. Si les colonnes de la recherche sont indexées
et que l’index est compatible avec les options de recherche spécifiées, Locate
utilise cet index.

Présentation des ensembles de données 18-17


Recherche dans les ensembles de données

Utilisation de la méthode Lookup


La méthode Lookup recherche la première ligne qui correspond au critère spécifié.
Si elle trouve une ligne correspondante, elle force le recalcul de tous les champs
calculés et de tous les champs de référence associés à l’ensemble de données,
puis elle renvoie un ou plusieurs champs de la ligne correspondante. Lookup ne
déplace pas le curseur sur cette ligne ; elle ne fait que renvoyer certaines de ses
valeurs.
Dans sa forme la plus simple, vous transmettez à Lookup le nom du champ de
recherche, une valeur de champ pour établir la correspondance et le champ ou
les champs à renvoyer. Par exemple, le code suivant recherche le premier
enregistrement de CustTable pour laquelle la valeur dans le champ Company est
Professional Divers, Ltd., puis renvoie le nom de la société, le nom du contact
commercial et son numéro de téléphone :
var
LookupResults: Variant;
begin
with CustTable do
LookupResults := Lookup('Company', 'Professional Divers, Ltd.', 'Company;
Contact; Phone');
end;
Lookup renvoie les valeurs des champs spécifiés du premier enregistrement
qu’elle trouve. Les valeurs sont renvoyées en tant que variants. Si plusieurs
valeurs ont été demandées, Lookup renvoie un tableau de variants. S’il n’existe
aucun enregistrement correspondant, Lookup renvoie un variant Null. Pour plus
d’informations concernant les tableaux de variants, voir l’aide en ligne.
Le vrai potentiel de Lookup se manifeste quand vous effectuez une recherche sur
plusieurs colonnes et que vous spécifiez plusieurs valeurs à rechercher. Pour
spécifier des chaînes contenant plusieurs colonnes ou des champs de résultats,
vous devez séparer les éléments de la chaîne par des points-virgules.
Les valeurs recherchées étant des variants, pour transmettre plusieurs valeurs,
vous devez soit transmettre un type tableau de variants comme argument (par
exemple, les valeurs renvoyées par la méthode Lookup), soit construire le tableau
de variants à la volée en utilisant la fonction VarArryOf. Le code suivant illustre
une recherche sur plusieurs colonnes :
var
LookupResults: Variant;
begin
with CustTable do
LookupResults := Lookup('Company; City', VarArrayOf(['Sight Diver', 'Christiansted']),
'Company; Addr1; Addr2; State; Zip');
end;
Lookup utilise la méthode de recherche la plus rapide pour trouver les
correspondances d’enregistrements. Si les colonnes de la recherche sont indexées,
Lookup utilise l’index.

18-18 Guide du développeur


Affichage et édition d’ensembles de données en utilisant des filtres

Affichage et édition d’ensembles de données en utilisant des


filtres
Il arrive souvent qu’une application ne s’intéresse qu’à un sous-ensemble
d’enregistrements d’un ensemble de données. Par exemple, votre application
peut souhaiter récupérer ou visualiser dans une base de données des clients les
enregistrements des sociétés dont le siège se trouve en Californie, ou bien
rechercher un enregistrement contenant un ensemble de valeurs de champ
déterminé. Dans tous les cas, vous pouvez filtrer les tables et les requêtes pour
gérer ces deux sortes d’opérations. Les filtres restreignent temporairement l’accès
à un sous-ensemble d’enregistrements d’un ensemble de données.
Le filtre spécifie des conditions qu’un enregistrement doit satisfaire pour être
affiché. Les conditions de filtre peuvent être stipulées dans la propriété Filter de
l’ensemble de données ou codées dans son gestionnaire d’événement
OnFilterRecord. Les conditions de filtre sont basées sur les valeurs contenues dans
un nombre quelconque de champs d’un ensemble de données, que ces champs
soient indexés ou non. Ainsi, pour ne visualiser que les enregistrements
correspondant à des entreprises situées en Californie, un filtre simple consistera à
rechercher les enregistrements contenant la valeur “CA” dans le champ Etat.
Remarque Les filtres sont appliqués à chaque enregistrement récupéré dans l’ensemble de
données. Pour extraire des volumes importants de données, il est plus efficace
d’utiliser une requête ou de définir une portée sur une table indexée pour
restreindre la récupération des enregistrements.

Activation et désactivation des filtres


L’activation d’un filtre sur un ensemble de données est un processus qui se
déroule en trois étapes :
1 Créez le filtre.
2 Si nécessaire, définissez des options de filtre pour les tests de filtres basés sur
des chaînes.
3 Mettez la propriété Filtered à True.
Lorsque le filtrage est activé, seuls les enregistrements correspondant au critère
de filtre sont disponibles pour une application. Le filtrage est toujours une
condition temporaire. Il peut être désactivé en mettant la propriété Filtered à
False.

Création de filtres
Les deux méthodes suivantes permettent de créer un filtre pour un ensemble de
données :
• Spécifiez des conditions de filtre dans la propriété Filter. Filter est
particulièrement utile pour créer et appliquer des filtres à l’exécution.

Présentation des ensembles de données 18-19


Affichage et édition d’ensembles de données en utilisant des filtres

• Ecrivez un gestionnaire d’événement OnFilterRecord pour les conditions de


filtre simples ou complexes. Avec OnFilterRecord, les conditions de filtre sont
spécifiées en mode conception. A la différence de la propriété Filter, qui se
limite à une seule chaîne contenant une logique de filtre, l’événement
OnFilterRecord peut utiliser la logique des branchements et des boucles pour
créer des conditions de filtre multiniveaux et complexes.
Lorsque vous créez des filtres en utilisant la propriété Filter, votre application
peut créer, modifier et appliquer des filtres dynamiquement (en réponse à des
saisies utilisateur, par exemple). L’inconvénient est que les conditions de filtre
doivent pouvoir s’exprimer dans une seule chaîne texte, ne peuvent pas utiliser
des constructions à base de branchements ou de boucles, et que leurs valeurs ne
peuvent être ni testées ni comparées à des valeurs ne figurant pas encore dans
l’ensemble de données.
La puissance de l’événement OnFilterRecord réside dans la possibilité pour un
filtre d’être complexe et variable, d’être basé sur plusieurs lignes de code
utilisant des constructions de branchements et de boucles, et de pouvoir tester et
comparer les valeurs de l’ensemble de données avec des valeurs figurant à
l’extérieur de l’ensemble de données, comme le texte d’une boîte de saisie. Son
inconvénient est que le filtre doit être défini en mode conception et qu’il ne peut
pas être modifié en réponse à des saisies utilisateur. Vous pouvez toutefois créer
plusieurs gestionnaires de filtres et permuter de l’un à l’autre en réponse à
certaines conditions d’application.
Les sections suivantes décrivent comment créer des filtres en utilisant la
propriété Filter et le gestionnaire d’événement OnFilterRecord.

Définition de la propriété Filter


Pour créer un filtre en utilisant la propriété Filter, vous devez spécifier une
chaîne contenant des conditions de filtre comme valeur de cette propriété. Par
exemple, l’instruction suivante crée un filtre qui a pour effet de tester le champ
State d’un ensemble de données pour voir s’il contient une valeur correspondant
à l’état de Californie :
Dataset1.Filter := 'State = ' + QuotedStr('CA');
La valeur de la propriété Filter peut aussi être définie à partir du texte saisi dans
un contrôle. Par exemple, l’instruction suivante affecte le texte d’une boîte de
saisie à la propriété Filter :
Dataset1.Filter := Edit1.Text;
Vous pouvez aussi créer une chaîne à partir de texte codé en dur et de données
saisies par l’utilisateur dans un contrôle :
Dataset1.Filter := 'State = ' + QuotedStr(Edit1.Text);
Après avoir spécifié une valeur pour la propriété Filter, mettez la propriété
Filtered à True.

18-20 Guide du développeur


Affichage et édition d’ensembles de données en utilisant des filtres

Les valeurs des champs peuvent aussi être comparées à des littéraux et à des
constantes en utilisant les opérateurs logiques et les opérateurs de comparaison
suivants :

Tableau 18.4 Opérateurs de comparaison et opérateurs logiques pouvant apparaître dans un filtre
Opérateur Signification
< Inférieur
> Supérieur
>= Supérieur ou égal
<= Inférieur ou égal
= Egal
<> Différent
AND Teste si deux instructions sont à True
NOT Vérifie que l’instruction suivante n’est pas à True
OR Vérifie qu’au moins une des deux instructions est à True

En utilisant des combinaisons de ces opérateurs, il est possible de créer des


filtres sophistiqués. Par exemple, l’instruction suivante vérifie que deux
conditions de test sont remplies avant d’afficher un enregistrement :
(Custno > 1400) AND (Custno < 1500);
Remarque Lorsque le filtrage est activé, les modifications apportées par l’utilisateur peuvent
ensuite faire en sorte que l’enregistrement ne réponde plus aux conditions de
filtre. La prochaine fois que l’enregistrement sera extrait de l’ensemble de
données, il n’apparaîtra plus. Dans ce cas, le prochain enregistrement suivant
répondant à la condition de filtre devient l’enregistrement en cours.

Ecriture d’un gestionnaire d’événement OnFilterRecord


Le filtre d’un ensemble de données est un gestionnaire d’événement qui répond
aux événements OnFilterRecord générés par l’ensemble à chaque récupération
d’enregistrement. Au cœur de tout gestionnaire de filtre se trouve un test qui
détermine si l’enregistrement est inclus dans ceux qui sont visibles dans
l’application.
Pour indiquer qu’un enregistrement répond à une condition de filtre, votre
gestionnaire doit basculer le paramètre Accept à True afin d’inclure
l’enregistrement, ou bien à False afin de l’exclure.
Par exemple, le filtre suivant affiche uniquement les enregistrements pour
lesquels le champ State vaut CA :
procedure TForm1.Table1FilterRecord(DataSet: TDataSet; var Accept: Boolean);
begin
Accept := DataSet['State'] = 'CA';
end;

Présentation des ensembles de données 18-21


Affichage et édition d’ensembles de données en utilisant des filtres

Quand le filtrage est activé, l’ensemble de données génère un événement


OnFilterRecord pour chaque enregistrement récupéré. Le gestionnaire d’événement
teste chaque enregistrement et seuls ceux répondant aux conditions de filtre
apparaissent dans l’application. Les performances étant directement dérivées du
nombre de fois que l’événement se déclenche et de la durée du traitement de
chaque événement, il est conseillé de réduire autant que possible le code du
gestionnaire d’événement OnFilterRecord .

Permutation entre les gestionnaires d’événement filtre à l’exécution


Vous pouvez coder un nombre quelconque de gestionnaires d’événements filtre
et permuter de l’un à l’autre à l’exécution. Pour passer à un autre gestionnaire
d’événement à l’exécution, affectez le nouveau gestionnaire d’événement à la
propriété OnFilterRecord de l’ensemble de données.
Par exemple, les instructions suivantes permettent de passer à un gestionnaire
d’événement OnFilterRecord appelé NewYorkFilter :
DataSet1.OnFilterRecord := NewYorkFilter;
Refresh;

Définition d’options de filtre


La propriété FilterOptions permet de spécifier si un filtre comparant des champs
basés sur des chaînes accepte des enregistrements à partir de comparaisons
partielles et si les comparaisons chaîne tiennent compte de la distinction
majuscules/minuscules. FilterOptions est une propriété ensemble qui peut être un
ensemble vide (la valeur par défaut) ou contenir l’une des valeurs suivantes (ou
les deux) :

Tableau 18.5 Valeurs de FilterOptions


Valeur Signification
foCaseInsensitive Ignore les différences majuscules/minuscules lors de la comparaison des
chaînes.
foPartialCompare Désactive les correspondances de chaînes partielles (aucune
correspondance ne peut être établie avec les chaînes se terminant par un
astérisque).

Par exemple, les instructions suivantes définissent un filtre qui ignore les
différences majuscules/minuscules lors de la comparaison des valeurs d’un
champ State :
FilterOptions := [foCaseInsensitive];
Filter := '''State'' = ''CA''';

18-22 Guide du développeur


Affichage et édition d’ensembles de données en utilisant des filtres

Navigation parmi les enregistrements d’un ensemble de données


filtré
Quatre méthodes permettent de se déplacer parmi les enregistrements d’un
ensemble de données filtré. Le tableau suivant dresse la liste de ces méthodes et
décrit leur utilisation :

Tableau 18.6 Méthodes navigationnelles relatives aux ensembles de données filtrés


Méthode Utilisation
FindFirst Se positionne sur le premier enregistrement de l’ensemble de données
correspondant au critère de filtre en cours. Cette recherche commence
toujours par le premier enregistrement de l’ensemble de données non filtré.
FindLast Se positionne sur le dernier enregistrement de l’ensemble de données
correspondant au critère de filtre en cours.
FindNext Se déplace de l’enregistrement en cours dans l’ensemble de données filtré sur
le prochain enregistrement.
FindPrior Se déplace de l’enregistrement en cours dans l’ensemble de données filtré sur
l’enregistrement précédent.

Par exemple, l’instruction suivante trouve le premier enregistrement filtré dans


un ensemble de données :
DataSet1.FindFirst;
Dans la mesure où la propriété Filter a été définie ou que vous avez créé un
gestionnaire d’événement OnFilterRecord pour votre application, ces méthodes
positionnent le curseur sur l’enregistrement spécifié, que le filtre soit ou non
activé pour l’ensemble de données. Si ces méthodes sont appelées lorsque le
filtrage n’est pas activé, elles provoquent les effets suivants :
1 Le filtrage est temporairement activé.
2 En cas de correspondance, le curseur est positionné sur un enregistrement.
3 Le filtrage est désactivé.
Remarque Si le filtrage est désactivé, si la propriété Filter n’a pas été définie et si aucun
gestionnaire d’événement OnFilterRecord n’a été créé, ces méthodes ont le même
effet que First(), Last(), Next() et Prior().
Toutes les méthodes navigationnelles relatives aux filtres positionnent le curseur
sur un enregistrement (si un enregistrement correspond au filtre spécifié), le
traitent comme l’enregistrement en cours et renvoient True. Si aucun
enregistrement ne correspond au filtre, la position du curseur reste inchangée, et
les méthodes renvoient False. En vérifiant l’état de la propriété Found vous
pouvez boucler ces appels et n’agir que lorsque Found vaut True. Ainsi, si le
curseur est déjà positionné sur le dernier enregistrement de l’ensemble de
données lorsque vous appelez FindNext, la méthode renvoie False, et
l’enregistrement en cours reste le même.

Présentation des ensembles de données 18-23


Modification des données

Modification des données


Les méthodes suivantes des ensembles de données permettent à une application
d’insérer, de mettre à jour et de supprimer des données :

Tableau 18.7 Méthodes des ensembles de données pour éditer des données
Méthode Description
Edit Met l’ensemble de données à l’état dsEdit s’il n’est pas déjà à l’état dsEdit ou
dsInsert.
Append Emet les données en suspens, déplace le curseur à la fin de l’ensemble de
données, puis met ce dernier à l’état dsInsert.
Insert Emet les données en suspens, puis met l’ensemble de données à l’état dsInsert.
Post Tente d’émettre l’enregistrement nouveau ou modifié vers la base de données. Si
l’opération réussit, l’ensemble de données est mis à l’état dsBrowse ; dans le cas
contraire, son état en cours reste inchangé.
Cancel Annule l’opération en cours et met l’ensemble de données à l’état dsBrowse.
Delete Supprime l’enregistrement en cours et met l’ensemble de données à l’état dsBrowse.

Modification d’enregistrements
Un ensemble de données doit être en mode dsEdit pour que l’application puisse
modifier des enregistrements. Dans votre code, vous pouvez utiliser la méthode
Edit pour mettre un ensemble de données en mode dsEdit si la propriété
CanModify de l’ensemble de données, accessible seulement en lecture, est à True.
C’est le cas si les tables sous-jacentes à l’ensemble de données reconnaissent les
droits d’accès en lecture et écriture.
Dans les fiches de votre application, certains contrôles orientés données pourront
mettre automatiquement votre ensemble de données à l’état dsEdit si les
conditions suivantes sont réunies :
• La propriété ReadOnly du contrôle est à False (la valeur par défaut).
• La propriété AutoEdit de la source de données du contrôle est à True.
• La propriété CanModify de l’ensemble de données est à True.
Important Pour les composants TTable dont la propriété ReadOnly vaut True et les
composants TQuery dont la propriété RequestLive vaut False, CanModify est à False
et empêche l’édition des enregistrements.
Remarque Même si un ensemble de données est à l’état dsEdit, la modification
d’enregistrements peut échouer dans le cas d’une base de données SQL si
l’utilisateur de votre application ne dispose pas des droits d’accès SQL appropriés.
Quand un ensemble de données se trouve en mode dsEdit, l’utilisateur peut
modifier toutes les valeurs de champ de l’enregistrement qui apparaît dans les
contrôles orientés données d’une fiche. Les contrôles orientés données, pour
lesquels la modification est activée automatiquement, appellent Post quand
l’utilisateur accomplit une action qui change la position du curseur (comme le
déplacement vers un autre enregistrement dans une grille).

18-24 Guide du développeur


Modification des données

Si vous avez fourni un composant navigateur dans vos fiches, l’utilisateur peut
annuler les modifications en cliquant sur le bouton Annuler du navigateur.
L’annulation des modifications renvoie l’ensemble de données à l’état dsBrowse.
Dans votre code, vous devez valider ou annuler les modifications en appelant les
méthodes appropriées. Les modifications sont validées en appelant Post. Vous les
annulez en appelant Cancel. Les méthodes Edit et Post sont souvent utilisées
conjointement. Considérons l’exemple suivant :
with CustTable do
begin
Edit;
FieldValues['CustNo'] := 1234;
Post;
end;
Dans cet exemple, la première ligne du fragment de code place l’ensemble de
données en mode dsEdit. La ligne suivante affecte la chaîne 1234 au champ
CustNo de l’enregistrement en cours. Pour finir, la dernière ligne écrit (émet)
l’enregistrement vers la base de données.
Remarque Si la propriété CachedUpdates d’un ensemble de données est à True, les
modifications émises sont écrites dans un tampon temporaire. Pour écrire les
modifications en mémoire cache dans la base de données, appelez la méthode
ApplyUpdates de l’ensemble de données. Pour plus d’informations concernant les
mises à jour en mémoire cache, voir chapitre 25, “Manipulation des mises à jour
en mémoire cache”.

Ajout de nouveaux enregistrements


Un ensemble de données doit être en mode dsInsert pour que l’application puisse
ajouter de nouveaux enregistrements. Dans votre code, vous pouvez utiliser la
méthode Insert ou Append pour mettre un ensemble de données en mode dsInsert
si la propriété CanModify de l’ensemble de données, accessible seulement en
lecture, est à True. C’est le cas si la base de données sous-jacente à l’ensemble de
données reconnaît les droits d’accès en lecture et écriture.
Dans les fiches de votre application, les contrôles orientés données grille et
navigateur pourront mettre automatiquement votre ensemble de données à l’état
dsInsert si les conditions suivantes sont réunies :
• La propriété ReadOnly du contrôle est à False (la valeur par défaut), et
• La propriété CanModify de l’ensemble de données est à True.
Quand un ensemble de données se trouve en mode dsInsert, l’utilisateur ou bien
l’application peut entrer des valeurs dans les champs associés au nouvel
enregistrement. Sauf pour les contrôles grille et navigateur, il n’y a aucune
différence apparente entre Insert et Append. Lors d’un appel Insert, une ligne vide
apparaît dans la grille au-dessus de l’enregistrement en cours. Lors d’un appel à
Append, la grille défile jusqu’au dernier enregistrement de l’ensemble de données,
une ligne vide apparaît au bas de la grille, et les boutons Suivant et Précédent
sont grisés sur tout composant navigateur associé à l’ensemble de données.

Présentation des ensembles de données 18-25


Modification des données

Les contrôles orientés données, pour lesquels l’insertion est activée


automatiquement, appellent Post quand l’utilisateur accomplit une action qui
change la position du curseur (comme le déplacement vers un autre
enregistrement dans une grille). Vous devez sinon appeler Post dans votre code.
La méthode Post écrit le nouvel enregistrement soit directement dans la base de
données, soit dans un tampon en mémoire si les mises à jour en mémoire cache
ont été activées. Pour écrire les modifications en mémoire cache dans la base de
données, vous devez appeler la méthode ApplyUpdates de l’ensemble de données.

Insertion d’enregistrements
Insert ouvre un nouvel enregistrement vide avant l’enregistrement en cours, puis
positionne le curseur sur ce nouvel enregistrement de façon à que les valeurs de
champ puissent être entrées par l’utilisateur ou par le code de votre application.
Quand une application appelle Post (ou ApplyUpdates si les mises à jour en
mémoire cache sont activées), le nouvel enregistrement inséré peut être écrit de
trois façons dans la base de données :
• Pour les tables Paradox ou dBASE indexées, l’enregistrement est inséré dans
l’ensemble de données à une position déterminée par l’index.
• Pour les tables non indexées, l’enregistrement est inséré dans l’ensemble de
données à sa position actuelle.
• Pour les bases de données SQL, l’emplacement physique où a lieu l’insertion
dépend de l’implémentation. Si la table est indexée, l’index est mis à jour avec
les informations du nouvel enregistrement.

Ajout d’enregistrements
Append ouvre un nouvel enregistrement vide à la fin de l’ensemble de données,
en faisant en sorte que l’enregistrement vide devienne l’enregistrement en cours
afin que les valeurs de champ de l’enregistrement puissent être entrées par
l’utilisateur ou par le code de votre application.
Quand une application appelle Post (ou ApplyUpdates si les mises à jour en
mémoire cache sont activées), le nouvel enregistrement ajouté peut être écrit de
trois façons dans la base de données :
• Pour les tables Paradox ou dBASE indexées, l’enregistrement est inséré dans
l’ensemble de données à une position déterminée par l’index.
• Pour les tables non indexées, l’enregistrement est ajouté à la fin de l’ensemble
de données.
• Pour les bases de données SQL, l’emplacement physique où a lieu l’ajout
dépend de l’implémentation. Si la table est indexée, l’index est mis à jour avec
les données du nouvel enregistrement.

18-26 Guide du développeur


Modification des données

Suppression d’enregistrements
Un ensemble de données doit être actif pour que l’application puisse supprimer
des enregistrements. Delete supprime l’enregistrement en cours dans l’ensemble
de données en mettant ce dernier en mode dsBrowse. L’enregistrement qui suit
celui supprimé devient l’enregistrement en cours. Si les mises à jour en mémoire
cache sont activées pour l’ensemble de données, l’enregistrement n’est réellement
supprimé qu’après l’appel de ApplyUpdates.
Si vous avez doté vos fiches d’un composant navigateur, les utilisateurs pourront
supprimer l’enregistrement en cours en cliquant sur le bouton d’effacement.
Dans votre code, vous devez appeler explicitement Delete pour supprimer
l’enregistrement en cours.

Validation des modifications


La méthode Post occupe une position centrale dans l’interaction entre
l’application Delphi et la table de la base de données. Post écrit les modifications
de l’enregistrement en cours, mais son comportement varie selon l’état de
l’ensemble de données.
• A l’état dsEdit, Post écrit un enregistrement modifié dans la base de données
(ou dans le tampon si les mises à jour en mémoire cache sont activées).
• A l’état dsInsert, Post écrit un nouvel enregistrement dans la base de données
(ou dans le tampon si les mises à jour en mémoire cache sont activées).
• A l’état dsSetKey, Post renvoie l’ensemble de données à l’état dsBrowse.
L’émission peut se faire, soit explicitement soit implicitement, comme partie
intégrante d’une autre procédure. Quand l’application quitte l’enregistrement en
cours, Post est appelée implicitement. Les appels aux méthodes First, Next, Prior,
et Last appellent Post si la table est en mode dsEdit ou dsInsert. En outre, les
méthodes Append et Insert émettent implicitement toute donnée en suspens.
Remarque La méthode Close n’appelle pas implicitement la méthode Post. Vous devez faire
appel à l’événement BeforeClose pour émettre explicitement toutes les
modifications en suspens.

Annulation des modifications


Une application peut à tout moment annuler les changements apportés à
l’enregistrement en cours, si elle n’a pas, directement ou indirectement, déjà
appelé Post. Par exemple, si un ensemble de données est en mode dsEdit et
qu’un utilisateur a changé les données d’un ou de plusieurs champs,
l’application peut rétablir les valeurs initiales de l’enregistrement en appelant la
méthode Cancel de l’ensemble de données. Un appel à Cancel renvoie toujours
l’ensemble de données à l’état dsBrowse.

Présentation des ensembles de données 18-27


Modification des données

Pour une fiche, vous pouvez permettre à l’utilisateur d’annuler les opérations de
modification, d’insertion, et d’ajout en incluant le bouton Annuler dans un
composant navigateur associé à l’ensemble de données, ou bien le code de votre
propre bouton d’annulation.

Modification d’enregistrements entiers


Dans les fiches, tous les contrôles orientés données, à l’exception des grilles et
des navigateurs, donnent accès à un champ unique d’enregistrement.
Par contre, dans votre code, vous pouvez utiliser les méthodes suivantes qui
fonctionnent sur des structures d’enregistrements entiers à condition toutefois
que la structure des tables de la base sous-jacente à l’ensemble de données soit
stable et ne subisse aucun changement. Le tableau suivant récapitule les
méthodes disponibles pour manipuler des enregistrements entiers plutôt que des
champs individuels de ces enregistrements :

Tableau 18.8 Méthodes qui opèrent sur des enregistrements entiers


Méthodes Description
AppendRecord([tableau de Ajoute un enregistrement avec les valeurs de colonne
valeurs]) spécifiées à la fin de la table ; analogue à Append. Exécute
implicitement Post.
InsertRecord([tableau de Insère un enregistrement avec les valeurs de colonne
valeurs]) spécifiées avant la position en cours du curseur dans la
table ; analogue à Insert. Exécute implicitement Post.
SetFields([tableau de valeurs]) Définit les valeurs des champs correspondants ; analogue à
l’affectation de valeurs aux composants TField. L’application
doit exécuter explicitement Post.

Ces méthodes acceptent comme argument un tableau de valeurs Tavern, où


chaque valeur correspond à une colonne de l’ensemble de données sous-jacent.
Utilisez la macro ARRAYOFCONST pour créer ces tableaux. Les valeurs peuvent
être littérales, variables ou NULL. Si le nombre de valeurs de l’argument est
inférieur au nombre de colonnes dans l’ensemble de données, les valeurs
restantes sont supposées avoir la valeur NULL.
Pour les ensembles de données non indexés, AppendRecord ajoute un
enregistrement à la fin de l’ensemble de données et InsertRecord insère un
enregistrement après la position en cours du curseur. Pour les tables indexées,
les deux méthodes placent l’enregistrement à la position déterminée par l’index.
Dans les deux cas, les deux méthodes déplacent le curseur sur le nouvel
enregistrement.
SetFields affecte les valeurs spécifiées dans le tableau de paramètres aux champs
de l’ensemble de données. Pour utiliser SetFields, l’application doit d’abord
appeler Edit pour mettre l’ensemble de données en mode dsEdit. Pour appliquer
les modifications à l’enregistrement en cours, elle doit exécuter Post.

18-28 Guide du développeur


Utilisation des événements des ensembles de données

Si vous utilisez SetFields pour modifier certains champs, et non tous les champs
d’un enregistrement existant, vous pouvez transmettre des valeurs NULL pour
les champs que vous ne voulez pas changer. Si vous ne fournissez pas un
nombre de valeurs correspondant au nombre de champs d’un enregistrement,
SetFields leur affecte la valeur NULL. Les valeurs NULL écrasent les valeurs
existantes de ces champs.
Par exemple, supposons qu’une base de données dispose d’une table COUNTRY
avec les colonnes Name, Capital, Continent, Area et Population. Si un composant
TTable appelé CountryTable, est lié à la table COUNTRY, l’instruction suivante
insère un enregistrement dans la table COUNTRY :
CountryTable.InsertRecord(['Japan', 'Tokyo', 'Asia']);
Cette instruction ne spécifie aucune valeur pour Area et Population, des valeurs
NULL leur sont donc affectées. La table est indexée sur Name, l’enregistrement
est donc inséré à la position alphabétique de “Japan”.
Pour mettre à jour l’enregistrement, l’application peut utiliser le code suivant :
with CountryTable do
begin
if Locate('Name', 'Japan', loCaseInsensitive) then;
begin
Edit;
SetFields(nil, nil, nil, 344567, 164700000);
Post;
end;
end;
Ce code affecte des valeurs aux champs Area et Population, avant de les émettre
dans la base de données. Les trois valeurs NULL agissent comme marqueurs de
remplissage des trois premières colonnes pour indiquer que leur contenu actuel
doit être préservé.
Attention Quand vous utilisez des pointeurs NULL avec SetFields afin de laisser intacte la
valeur de certains champs, il faut transtyper les NULL en void *. Si vous utilisez
un NULL comme paramètre sans effectuer le transtypage, vous affectez une
valeur vierge au champ.

Utilisation des événements des ensembles de données


Les ensembles de données disposent d’une gamme d’événements qui permettent
à une application d’accomplir des validations, des calculs de totaux, ainsi que
diverses autres tâches. Le tableau suivant dresse la liste de ces événements :
Tableau 18.9 Evénements relatifs aux ensembles de données
Evénement Description
BeforeOpen, AfterOpen Appelé avant/après l’ouverture de l’ensemble de données.
BeforeClose, AfterClose Appelé avant/après la fermeture de l’ensemble de données.
BeforeInsert, AfterInsert Appelé avant/après la bascule à l’état Insertion de l’ensemble de
données.

Présentation des ensembles de données 18-29


Utilisation des événements des ensembles de données

Tableau 18.9 Evénements relatifs aux ensembles de données (suite)


Evénement Description
BeforeEdit, AfterEdit Appelé avant/après la bascule à l’état Edition de l’ensemble de
données.
BeforePost, AfterPost Appelé avant/après l’émission des modifications d’une table.
BeforeCancel, AfterCancel Appelé avant/après l’annulation de l’état précédent.
BeforeDelete, AfterDelete Appelé avant/après la suppression d’un enregistrement.
OnNewRecord Appelé quand un nouvel enregistrement est créé ; utilisé pour
définir les valeurs par défaut.
OnCalcFields Appelé quand les champs calculés sont effectivement calculés.

Pour plus d’informations concernant les événements du composant TDataSet, voir


la référence VCL en ligne.

Interruption d’une méthode


Pour interrompre une méthode telle que Open ou Insert, appelez la procédure Abort
dans tout gestionnaire d’événement dont le nom débute par Before (BeforeOpen,
BeforeInsert, etc.). Par exemple, le code suivant demande à l’utilisateur de confirmer
une opération de suppression, faute de quoi l’appel à Delete est interrompu :
procedure TForm1.TableBeforeDelete (Dataset: TDataset)begin
if MessageDlg('Delete This Record?', mtConfirmation, mbYesNoCancel, 0) <> mrYes then
Abort;
end;

Utilisation de l’événement OnCalcFields


L’événement OnCalcFields est utilisé pour définir les valeurs des champs calculés.
La propriété AutoCalcFields détermine quand OnCalcFields est appelé. Si
AutoCalcFields vaut True, OnCalcFields est appelé quand :
• Un ensemble de données est ouvert.
• La focalisation se déplace d’un composant visuel à un autre, ou bien d’une
colonne à une autre dans un contrôle grille orienté données.
• Un enregistrement est récupéré dans la base de données.
OnCalcFields est toujours appelé quand une valeur dans un champ non calculé
change, quelle que soit la valeur d’AutoCalcFields.
Attention Comme OnCalcFields est fréquemment appelé, l’exécution du code que vous
écrivez pour ce gestionnaire doit être brève. En outre, si AutoCalcFields est à True,
OnCalcFields ne doit accomplir aucune action qui modifie l’ensemble de données
(ou l’ensemble de données lié s’il fait partie d’une relation maître-détail), car cela
peut induire une récursion. Par exemple, si OnCalcFields exécute Post, et que
AutoCalcFields est à True, OnCalcFields est à nouveau appelé, provoquant un
nouvel appel à Post, etc.

18-30 Guide du développeur


Utilisation des ensembles de données orientés BDE

Si AutoCalcFields vaut False, OnCalcFields n’est pas appelé lorsque des champs
individuels sont modifiés dans un enregistrement.
Pendant la durée de l’exécution de OnCalcFields, l’ensemble de données est en
mode dsCalcFields, et vous ne pouvez donc affecter de valeur à un champ que
s’il s’agit d’un champ calculé. L’exécution de OnCalcFields achevée, l’ensemble de
données revient à l’état dsBrowse.

Utilisation des ensembles de données orientés BDE


Les ensembles de données orientés BDE sont destinés aux composants ensemble
de données qui utilisent le moteur de bases de données Borland (BDE) pour
accéder aux données. L’orientation BDE est gérée dans TBDEDataSet, descendant
direct de TDataSet. En outre, des fonctionnalités de contrôle de base de données
et de session sont offertes par TDBDataSet, descendant direct de TBDEDataSet.
Figure 18.4 Hiérarchie des composants ensemble de données

TDataSet

TNestedTable

TClientDataSet TBDEDataSet

TQuery

TDBDataSet TStoredProc

TTable

Cette section présente les fonctionnalités d’ensemble de données de TBDEDataSet


et TDBDataSet. Vous êtes supposé connaître le composant TDataSet présenté plus
haut dans ce chapitre. Pour une présentation générale des ensembles de données
issus de TDataSet, voir le début de ce chapitre.
Remarque Bien que vous deviez comprendre les fonctionnalités de TBDEDataSet et
TDBDataSet, sauf si vous développez vos propres ensembles de données orientés
BDE, vous ne les utilisez jamais directement dans vos applications mais utilisez
les descendants directs de TDBDataSet : TQuery, TStoredProc et TTable. Pour des
informations particulières sur l’utilisation de TStoredProc, voir chapitre 22,
“Manipulation des procédures stockées” Pour des informations particulières sur
l’utilisation de TQuery, voir chapitre 21, “Manipulation des requêtes” Pour des
informations particulières sur TTable, voir chapitre 20, “Manipulation des tables”.

Présentation des ensembles de données 18-31


Utilisation des ensembles de données orientés BDE

Présentation de l’orientation BDE


Le composant TBDEDataSet implémente les méthodes abstraites de TDataSet qui
contrôlent la navigation dans les enregistrements, l’indexation et la définition des
signets. Il réimplémente aussi de nombreux événements et méthodes virtuels de
TDataSet pour tirer parti du BDE. Les implémentations propres au BDE des
fonctionnalités de TDataSet ne s’écartent pas de la description générale de
l’utilisation de ces fonctionnalités avec TDataSet. Pour plus d’informations à leur
sujet, voir au début de ce chapitre.
Outre les fonctionnalités propres au BDE communes à tous les ensembles de
données, TBDEDataSet offre de nouveaux événements, propriétés et méthodes
pour la gestion des BLOB et des mises à jour en mémoire cache ainsi que pour
la communication avec un serveur de bases de données distant. TDBDataSet offre
une méthode et des propriétés pour la gestion des connexions aux bases de
données et l’association d’un ensemble de données à une session BDE. Les
sections suivantes décrivent ces fonctionnalités et font référence à d’autres
sections du Guide du développeur qui traitent aussi de leur utilisation.

Gestion des connexions de base de données et de session


Le composant TDBDataSet offre les propriétés et la fonction suivantes pour
l’utilisation des connexions de base de données et de session :
Tableau 18.10 Propriétés et fonction de TDBDataSet relatives à l’utilisation des bases de données et
des sessions
Fonction ou propriété Utilisation
fonction CheckOpen Détermine si une base de données est ouverte. Renvoie True si la
connexion est active, False sinon.
Database Identifie le composant base de données auquel est associé
l’ensemble de données.
DBHandle Spécifie le handle de base de données BDE du composant base de
données spécifié dans la propriété Database. Utilisé uniquement en
cas d’appel API direct au BDE.
DBLocale Spécifie les informations BDE locales sur le composant base de
données spécifié dans la propriété Database Utilisé uniquement en
cas d’appel API direct au BDE.
DBSession Spécifie le handle de session BDE du composant session spécifié par
la propriété SessionName. Utilisé uniquement en cas d’appel API
direct au BDE.
DatabaseName Spécifie l’alias BDE ou le nom du composant base de données de la
base de données utilisée par cet ensemble de données. Si l’ensemble
de données est une table Paradox ou dBASE, DatabaseName peut
être la spécification complète du chemin d’accès menant au
répertoire de la base de données.
SessionName Spécifie la session à laquelle est associé ce composant ensemble de
données. Si vous utilisez à la fois des composants base de données
et session avec un ensemble de données, la valeur de SessionName
doit être la même que celle de la propriété SessionName du
composant base de données.

18-32 Guide du développeur


Utilisation des ensembles de données orientés BDE

Utilisation des propriétés DatabaseName et SessionName


Les propriétés de base de données et de session de TDBDataSet les plus utilisées
sont DatabaseName et SessionName. Si vous utilisez des bases de données sur un
serveur de bases de données distant, tel que Sybase, Oracle ou InterBase, votre
application gère généralement cette connexion par le biais d’un composant
TDatabase. Vous devez définir la propriété DatabaseName de chaque ensemble de
données en fonction du nom du composant base de données qui établit la
connexion entre l’ensemble de données et la base de données qu’il utilise. Si
vous n’utilisez pas de composants base de données, DatabaseName doit avoir
pour valeur un alias BDE (ou, éventuellement, la spécification complète d’un
chemin d’accès à une table dBASE ou Paradox).
SessionName indique la session BDE à laquelle doit être associé un ensemble de
données. Si vous n’utilisez pas de composants session explicites dans votre
application, vous ne devez pas fournir de valeur pour cette propriété. La valeur
est automatiquement fournie. Si votre application fournit plusieurs sessions, vous
pouvez définir la propriété SessionName d’un ensemble de données en fonction
de la propriété SessionName du composant session approprié dans votre
application. Si votre application utilise à la fois plusieurs composants session et
un ou plusieurs composants base de données, la propriété SessionName d’un
ensemble de données doit correspondre à la propriété SessionName du composant
base de données auquel est associé l’ensemble de données.
Pour plus d’informations sur la gestion des connexions de base de données avec
TDatabase, voir chapitre 17, “Connexion aux bases de données”. Pour plus
d’informations sur la gestion des sessions avec TSession et TSessionList, voir
chapitre 16, “Gestion de sessions de bases de données”.

Utilisation des propriétés de handle BDE


A moins que vous passiez outre la fonctionnalité intégrée des composants
ensemble de données et réalisiez des appels API directs au BDE, vous n’avez pas
besoin d’utiliser les propriétés DBHandle, DBLocale et DBSession. Ces propriétés
sont des propriétés en lecture seule automatiquement affectées à un ensemble de
données lorsqu’il est connecté à un serveur de bases de données par le biais du
BDE. Ces propriétés sont fournies en tant que ressource pour les développeurs
d’applications ayant besoin de faire des appels API directs aux fonctions BDE,
dont certaines acceptent des paramètres handle. Pour plus d’informations sur
l’API BDE, voir le fichier d’aide en ligne, BDE32.HLP.

Utilisation des mises à jour en mémoire cache


La mise en mémoire cache des mises à jour vous permet d’extraire des données
d’une base de données, de les placer en mémoire cache pour les modifier
localement puis d’appliquer les mises à jour en mémoire cache à la base de
données en tant qu’unité. Lorsque les mises à jour en mémoire cache sont
activées, les mises à jour apportées à un ensemble de données (comme la
validation de modifications ou la suppression d’enregistrements) sont stockées
dans une mémoire cache interne au lieu d’être écrites directement dans la table

Présentation des ensembles de données 18-33


Utilisation des ensembles de données orientés BDE

sous-jacente de l’ensemble de données. Lorsque les modifications sont achevées,


votre application appelle une méthode qui écrit les mises à jour en mémoire
cache dans la base de données et efface le contenu de la mémoire cache.
L’approche recommandée lors des mises à jour à partir de la mémoire cache
consiste à utiliser un ensemble de données client plutôt qu’un ensemble de
données BDE. Néanmoins, TBDEDataSet offre une autre approche, avec des
méthodes intégrées pour la gestion des mises à jour à partir de la mémoire
cache. Le tableau suivant présente les propriétés, événements et méthodes
impliqués dans la mise à jour à partir de la mémoire cache :
Tableau 18.11 Propriétés, événements et méthodes des mises à jour en mémoire cache
Propriété, événement
ou méthode Utilisation
CachedUpdates (propriété) Détermine si les mises à jour en mémoire cache prennent effet
pour l’ensemble de données. Si la propriété vaut True, la mise
à jour à partir de la mémoire cache est activée. Si elle vaut
False, la mise à jour n’est pas activée.
UpdateObject (propriété) Indique le nom du composant TUpdateSQL utilisé pour mettre
à jour les ensembles de données en fonction de requêtes.
UpdatesPending (propriété) Indique si la mémoire cache locale contient des enregistrements
mis à jour qui doivent être appliqués à la base de données.
True indique que certains enregistrements sont à mettre à jour.
False indique que la mémoire cache est vide.
UpdateRecordTypes Indique le type d’enregistrements mis à jour qu’il faut rendre
(propriété) visible à l’application pendant l’application des mises à jour
placées en mémoire cache.
UpdateStatus (méthode) Indique si un enregistrement est inchangé, modifié, inséré ou
supprimé.
OnUpdateError (événement) Procédure créée par le développeur qui gère les erreurs de
mise à jour enregistrement par enregistrement.
OnUpdateRecord Procédure créée par le développeur qui traite les mises à jour
(événement) enregistrement par enregistrement.
ApplyUpdates (méthode) Applique à la base de données les enregistrements contenus
dans la mémoire cache locale.
CancelUpdates (méthode) Supprime de la mémoire cache locale toutes les mises à jour en
suspens sans les appliquer à la base de données.
CommitUpdates (méthode) Efface le contenu de la mémoire cache après application réussie
des mises à jour.
FetchAll (méthode) Copie tous les enregistrements de la base de données dans la
mémoire cache locale pour modification et mise à jour.
RevertRecord (méthode) Annule la mise à jour de l’enregistrement en cours si elle n’a
pas été appliquée côté serveur.

L’utilisation des mises à jour en mémoire cache et leur coordination avec d’autres
applications accèdant aux données dans un environnement multi-utilisateur
constituent un sujet complexe entièrement abordé au chapitre 25, “Manipulation des
mises à jour en mémoire cache.”
Pour plus d’informations sur l’utilisation d’un ensemble de données client, voir
chapitre 24, “Création et utilisation d’un ensemble de données client”.

18-34 Guide du développeur


Utilisation des ensembles de données orientés BDE

Mise en mémoire cache des BLOB


TBDEDataSet offre la propriété CacheBlobs qui permet de contrôler la mise en
mémoire cache locale des champs BLOB par le BDE lorsqu’une application lit les
enregistrements BLOB. Par défaut, CacheBlobs vaut True, ce qui signifie que le
BDE place en mémoire cache une copie locale des champs BLOB. La mise en
mémoire cache des BLOB améliore les performances de l’application en
permettant au BDE de stocker des copies locales des BLOB au lieu d’aller les
chercher systématiquement sur un serveur de bases de données à mesure qu’un
utilisateur parcourt les enregistrements.
Dans les applications et les environnements où les BLOB sont souvent mis à jour
ou remplacés, et où une vue actualisée des données BLOB est plus importante
que la performance de l’application, vous pouvez initialiser CacheBlobs à False
afin que votre application voie toujours la version la plus récente d’un champ
BLOB.

Présentation des ensembles de données 18-35


18-36 Guide du développeur
Chapitre

Manipulation des composants champ


Chapter 19
19
Ce chapitre décrit les propriétés, les événements et les méthodes communes à
l’objet TField et ses descendants. Ces derniers représentent les colonnes de base
de données des ensembles de données. Ce chapitre décrit également comment
utiliser les composants champ descendants pour contrôler l’affichage et l’édition
des données dans des applications.
Les composants TField ne sont jamais directement utilisés dans vos applications.
Par défaut, un composant descendant de TField est affecté à tout ensemble de
données placé dans une application et ouvert. Ce descendant de TField,
dynamique et spécifique au type de données, représente chaque colonne d’une
table de base de données. Pendant la phase de conception, il est possible de
surcharger les valeurs par défaut des champs dynamiques en appelant l’éditeur
de champs pour créer des champs.
Le tableau suivant dresse la liste des différents types de descendants du
composant TField, explique leur utilisation et les valeurs autorisées :

Tableau 19.1 Composants champ


Nom du composant Utilisation
TADTField Champ ADT (Abstract Data Type).
TAggregateField Champ agrégat géré dans un ensemble de données client.
TArrayField Champ tableau.
TAutoIncField Nombre entier compris entre -2 147 483 648 et 2 147 483 647. Utilisé
dans Paradox pour les champs dont les valeurs sont automatiquement
incrémentées.
TBCDField Nombres réels suivis d’un certain nombre de chiffres après le
séparateur décimal. La précision porte sur 18 chiffres. La portée
dépend du nombre de chiffres après le séparateur décimal.
TBooleanField Valeur True ou False.
TBlobField Données binaires : limite maximale théorique de 2 Go.

Manipulation des composants champ 19-1


Présentation des composants champ

Tableau 19.1 Composants champ (suite)


Nom du composant Utilisation
TBytesField Données binaires : limite maximale théorique de 2 Go.
TCurrencyField Nombres réels compris entre 5,0 * 10-324 et 1,7 * 10308. Utilisé dans
Paradox pour les champs ayant une précision de deux décimales.
TDataSetField Valeur d’ensemble de données imbriqué.
TDateField Valeur date.
TDateTimeField Valeur date et heure.
TFloatField Nombres réels allant de 5,0 * 10-324 à 1,7 * 10308.
TBytesField Données binaires : nombre maximum d’octets : 255.
TBytesField Nombre entier compris entre -2 147 483 648 et 2 147 483 647.
TLargeintField Nombres entiers compris entre 2-63 et 2 63
.
TMemoField Données texte : limite maximale théorique de 2 Go.
TNumericField Nombres réels allant de 3,4 * 10-4932 à 1,1 * 104932.
TReferenceField Pointeur sur un objet base de données relationnelles.
TSmallintField Nombre entier compris entre -32 768 et 32 768.
TStringField Données chaîne : taille maximale en octets : 8 192, y compris les
caractères à terminaison nulle.
TTimeField Valeur heure.
TVarBytesField Données binaires : nombre maximum d’octets : 255.
TWordField Nombres entiers compris entre 0 et 65 535.

Ce chapitre présente les propriétés et les méthodes héritées de TField. Dans la


plupart des cas, TField déclare ou implémente une fonctionnalité standard
surchargée par les objets descendants. Lorsque plusieurs objets descendant
partagent une fonctionnalité surchargée, nous vous le signalons. Pour des
informations détaillées sur chaque composant champ, voir le Guide de référence de
la bibliothèque de composants visuels.

Présentation des composants champ


A l’instar des autres composants d’accès aux données de Delphi, les composants
champ sont invisibles, même au moment de la conception. Au lieu de cela, ils
sont associés à un composant ensemble de données et fournissent aux
composants orientés données, tels que TDBEdit et TDBGrid, un accès aux
colonnes de base de données par l’intermédiaire de cet ensemble de données.
D’une façon générale, un composant champ représente des caractéristiques d’une
colonne d’un champ de base de données (comme le type de données ou la taille
du champ). Il représente également les caractéristiques d’affichage du champ,
comme l’alignement, le format d’affichage et le format d’édition. Pour finir,
lorsque vous passez d’un enregistrement à un autre dans un ensemble de
données, un composant champ vous permet de visualiser et de modifier la

19-2 Guide du développeur


Présentation des composants champ

valeur du champ dans l’enregistrement en cours. Par exemple, un composant


TFloatField est doté de quatre propriétés qui affectent directement l’apparence des
données :

Tableau 19.2 Propriétés du composant TFloatField affectant l’affichage des données


Propriété Utilisation
Alignment Centre ou aligne à droite ou à gauche les valeurs d’un contrôle pour
l’affichage.
DisplayWidth Spécifie le nombre de chiffres affichés dans un contrôle.
DisplayFormat Spécifie le formatage des données affichées (par exemple, le nombre de
décimales).
EditFormat Contrôle l’aspect des valeurs dans un contrôle au cours d’édition.

Les composants champ ont de nombreuses propriétés en commun (comme


DisplayWidth et Alignment) ; ils ont également des propriétés spécifiques à
certains types de données (telles que Precision et TFloatField). Chacune de ces
propriétés affecte l’aspect des données dans une fiche. Certaines d’entre elles,
comme Precision, peuvent également affecter les données pouvant être saisies par
un utilisateur quand il modifie ou saisit des données.
Tous les composants champ d’un ensemble de données sont soit dynamiques
(automatiquement générés à partir de la structure sous-jacente des tables de
bases de données) ou persistants (générés à partir des noms de champs et des
propriétés définis dans l’éditeur de champs). Les champs dynamiques et les
champs persistants ont des caractéristiques différentes et sont appropriés à des
situations précises. Les sections suivantes décrivent en détail les champs
persistants et les champs dynamiques et indiquent quand les utiliser.

Champs dynamiques
Les composants champ générés dynamiquement correspondent au comportement
par défaut. En fait, tous les composants champ d’un ensemble de données sont
créés dynamiquement à chaque fois que vous placez un ensemble de données
dans un module de données, puis l’associez à une base de données et l’ouvrez.
Un composant champ est dynamique s’il est créé automatiquement. Les
composants champ dynamiques sont créés par Delphi en fonction de la structure
physique sous-jacente des colonnes d’une ou de plusieurs tables connectées à un
ensemble de données. Delphi génère un composant champ pour chaque colonne
des requêtes ou des tables sous-jacentes. Le descendant TField créé pour chaque
colonne d’une table de base de données sous-jacente est déterminé par les
informations de type de champ envoyées par le BDE (Borland Database Engine)
ou par le composant fournisseur (pour les applications multiniveaux).
Le type d’un composant champ détermine ses propriétés et la façon dont les
données qui lui sont associées apparaissent dans les contrôles orientés données
d’une fiche. Les champs dynamiques sont temporaires, leur durée est limitée au
moment où l’ensemble de données est ouvert.

Manipulation des composants champ 19-3


Présentation des composants champ

A chaque nouvelle ouverture d’un ensemble de données utilisant des champs


dynamiques, Delphi reconstruit un ensemble de composants champ dynamiques
à partir de la structure des tables sous-jacentes. Si les colonnes de ces tables de
bases de données changent, à la prochaine ouverture d’un ensemble de données
utilisant des composants champ dynamiques, les composants champ générés
automatiquement changeront également.
Les champs dynamiques doivent être utilisés dans des applications flexibles en
matière d’édition et d’affichage des données. Par exemple, pour créer un outil
d’exploration des bases de données comme l’explorateur SQL, vous devez utiliser
des champs dynamiques, car chaque table de base de données n’a pas le même
nombre de colonnes et ses colonnes sont de types différents. Les champs
dynamiques s’utilisent aussi dans des applications où l’interaction utilisateur
avec les données se produit à l’intérieur des composants grille et lorsque les
tables de bases de données utilisées par l’application changent souvent.
Pour utiliser des champs dynamiques dans une application :
1 Placez des ensembles de données et des sources de données dans un module
de données.
2 Associez les ensembles de données aux tables de bases de données et aux
requêtes, et associez les sources de données aux ensembles de données.
3 Placez des contrôles orientés données dans les fiches de votre application,
ajoutez le module de données à chaque clause uses des unités de fiches, et
associez chaque contrôle orienté données à une source de données du module.
Il faut aussi associer un champ à chaque contrôle orienté données le
nécessitant.
4 Ouvrez les ensembles de données.
Les champs dynamiques présentent malgré tout quelques limites. Sans écrire de
code, il est impossible de changer leurs paramètres par défaut d’affichage et
d’édition, il est difficile de modifier leur ordre d’affichage sans prendre de risque
et il est impossible d’empêcher l’accès à des champs de l’ensemble de données.
Des champs supplémentaires comme les champs de référence ou les champs
calculés ne peuvent pas être créés pour l’ensemble de données et il est
impossible de surcharger le type par défaut d’un champ dynamique. Pour un
plus grand contrôle des champs de vos applications de bases de données, vous
devez appeler l’éditeur de champs et créer des champs persistants.

Champs persistants
Par défaut, les champs des ensembles de données sont des champs dynamiques.
Leurs propriétés et leur disponibilité sont automatiquement définies et ne
peuvent pas être modifiées. Pour contrôler les propriétés et les événements d’un
champ de façon à pouvoir définir ou modifier la visibilité du champ ou ses
caractéristiques d’affichage (que ce soit à la conception ou à l’exécution), créer de
nouveaux champs à partir des champs existants dans un ensemble de données
ou valider la saisie des données, vous devez créer des champs persistants.

19-4 Guide du développeur


Présentation des composants champ

Au moment de la conception vous pouvez, ce qui est conseillé, utiliser l’éditeur


de champs pour créer des listes persistantes de composants champ utilisés par
les ensembles de données de votre application. Ces listes sont stockées dans
votre application et ne changent pas, même si la structure de la base de données
sous-jacente d’un ensemble de données est modifiée.
La création de composants champ persistants offre les avantages suivants. Vous
pouvez :
• limiter les champs de votre ensemble de données à un sous-ensemble
correspondant aux colonnes de la base de données sous-jacente ;
• ajouter des composants champ à la liste des composants persistants ;
• retirer des composants champ de la liste des composants persistants pour
empêcher votre application d’accéder à des colonnes particulières d’une base
de données sous-jacente ;
• définir de nouveaux champs – généralement pour remplacer des champs
existants – d’après les colonnes de la table ou de la requête sous-jacente d’un
ensemble de données ;
• définir des champs calculés dont la valeur est dérivée d’autres champs de
l’ensemble de données ;
• définir des champs de référence qui calculent leur valeur d’après les champs
d’autres ensembles de données ;
• modifier l’affichage des composants champ et éditer leurs propriétés.
Un champ persistant est un champ généré par Delphi à partir des noms de
champ et des propriétés spécifiés dans l’éditeur de champs. Il est ensuite possible
de créer des gestionnaires d’événements pour ces champs de façon à ce qu’ils
puissent répondre aux modifications de données et aux validations de saisies.
Remarque Lorsque vous créez les champs persistants d’un ensemble de données, seuls ceux
sélectionnés sont disponibles pour votre application en mode conception et à
l’exécution. Pendant la phase de conception, vous pouvez toujours choisir Ajout
de champs dans l’éditeur de champs de façon à ajouter ou supprimer des
champs persistants pour un ensemble de données.
La totalité des champs utilisés dans un ensemble de données sont soit persistants
soit dynamiques. Il est impossible de combiner les deux types de champs au sein
du même ensemble de données. Si vous avez créé des champs persistants pour
un ensemble de données, vous devrez tous les supprimer pour revenir à des
champs dynamiques. Pour plus d’informations sur les champs dynamiques, voir
“Champs dynamiques” à la page 19-3.
Remarque L’une des principales utilisations des champs persistants est de contrôler l’aspect
et l’affichage des données. L’aspect des données peut également être affecté d’autres
façons. Vous pouvez utiliser le dictionnaire des données pour affecter des attributs
à un composant champ. Vous pouvez également contrôler l’aspect des colonnes
dans les grilles orientées données. Pour plus de détails sur le dictionnaire des
données, reportez-vous à “Création d’ensembles d’attributs pour les composants
champ” à la page 19-17. Pour savoir comment gérer l’aspect des colonnes dans les
grilles, reportez-vous à “Utilisation d’un contrôle grille à son état par défaut” à la
page 26-19.

Manipulation des composants champ 19-5


Création de champs persistants

Création de champs persistants


Les composants champ persistants créés avec l’éditeur de champs offrent un
accès en programmation aux données sous-jacentes. Ils garantissent qu’à chaque
exécution de votre application, celle-ci utilise et affiche toujours les mêmes
colonnes, dans le même ordre, même si la structure physique de la base de
données sous-jacente a changé. Les composants orientés données et le code du
programme qui dépendent de champs spécifiques fonctionnent toujours comme
prévu. Si une colonne dont dépend un composant champ persistant est
supprimée ou modifiée, Delphi provoque une exception au lieu de faire
fonctionner l’application avec une colonne inexistante ou avec des données sans
correspondance.
Pour créer un composant champ persistant pour un ensemble de données :
1 Placez un ensemble de données dans un module de données.
2 Définissez la propriété DatabaseName de l’ensemble de données.
3 Définissez la propriété TableName s’il s’agit d’un composant TTable ou la
propriété SQL s’il s’agit d’un composant TQuery.
4 Double-cliquez sur le composant ensemble de données dans le module de
données de façon à appeler l’éditeur de champs. Ce dernier contient une barre
de titre, des boutons de navigation et une boîte liste.
La barre de titre de l’éditeur de champs affiche le nom du module de données
ou de la fiche contenant l’ensemble de données, ainsi que le nom de celui-ci.
Ainsi, si vous ouvrez l’ensemble de données Customers dans le module de
données CustomerData, la barre de titre affiche ‘CustomerData.Customers’, ou
la partie du nom qu’elle peut contenir.
Sous la barre de titre figure un ensemble de boutons de navigation vous
permettant de faire défiler un à un les enregistrements d’un ensemble de
données au moment de la conception ; ces boutons vous permettent également
de passer directement au premier ou au dernier enregistrement. Les boutons
de navigation sont estompés si vous n’avez pas encore créé de composant
champ persistant pour l’ensemble de données ou si celui-ci n’est pas actif.
La boîte liste affiche le nom des composants champ persistants de l’ensemble
de données. Lorsque vous lancez l’éditeur de champs la première fois pour un
nouvel ensemble de données, la liste est vide car les composants champ sont
dynamiques et non persistants. Si vous lancez l’éditeur de champs pour un
ensemble de données contenant déjà des composants champ persistants, vous
verrez leur nom dans la boîte liste.
5 Choisissez Ajout de champs dans le menu contextuel de l’éditeur de champs.
6 Dans la boîte de dialogue Ajout de champs, sélectionnez les champs
persistants. Par défaut, tous les champs sont sélectionnés à l’ouverture de la
boîte de dialogue. Tous les champs que vous sélectionnerez dans cette boîte
de dialogue deviendront des champs persistants.

19-6 Guide du développeur


Modification de l’ordre des champs persistants

La boîte de dialogue Ajout de champs se referme et les champs que vous avez
sélectionnés apparaissent dans la boîte liste de l’éditeur de champs ; ce sont les
champs persistants. Si l’ensemble de données est actif, vous remarquerez
également que les boutons de navigation Suivant et Dernier au-dessus de la boîte
liste sont activés.
Désormais, à chaque fois que vous ouvrirez l’ensemble de données, Delphi ne
créera plus de composant champ dynamique pour chaque colonne de la base de
données sous-jacente. Au lieu de cela, il ne créera des composants persistants
que pour les champs spécifiés.
A chaque fois que vous ouvrirez l’ensemble de données, Delphi vérifiera que
chacun des champs persistants non calculés est présent ou qu’il peut être créé à
partir des données de la base. Sinon, il provoquera une exception vous
avertissant que le champ n’est pas valide et n’ouvrira pas l’ensemble de données.

Modification de l’ordre des champs persistants


L’ordre dans lequel apparaissent les composants champ persistants dans la boîte
liste de l’éditeur de champs est l’ordre par défaut dans lequel ils apparaissent
dans un composant grille orienté données. Vous pouvez le modifier en faisant
glisser les champs ailleurs dans la boîte liste.
Pour changer l’emplacement de champs :
1 Sélectionnez-les. Vous pouvez sélectionner et déplacer plusieurs champs à la fois.
2 Faites-les glisser au nouvel emplacement.
Si vous sélectionnez un ensemble de champs non contigus et les faites glisser
vers un nouvel emplacement, ils sont insérés en tant que bloc contigu. A
l’intérieur de ce bloc, l’ordre des champs reste inchangé.
Pour changer l’ordre d’un champ dans la liste, une autre méthode consiste à
sélectionner le champ et utiliser les touches Ctrl+Haut et Ctrl+Bas.

Définition de nouveaux champs persistants


Non seulement vous pouvez sélectionner des composants champ existants à
changer en composants champ persistants pour un ensemble de données, mais
aussi créer des champs persistants spéciaux supplémentaires ou destinés à
remplacer les autres champs persistants de l’ensemble de données. Le tableau
suivant dresse la liste des champs pouvant être créés :
Tableau 19.3 Types de champs persistants
Type de champ Utilisation
Champs de Ils remplacent généralement les champs existants (par exemple, pour
données changer le type des données d’un champ) et sont basés sur les colonnes de
la table ou de la requête sous-jacente d’un ensemble de données).
Champs calculés Ils affichent des valeurs calculées lors de l’exécution par le gestionnaire
d’événement OnCalcFields d’un ensemble de données.

Manipulation des composants champ 19-7


Définition de nouveaux champs persistants

Tableau 19.3 Types de champs persistants (suite)


Type de champ Utilisation
CalcInterne Ils affichent des valeurs calculées lors de l’exécution par le client ensemble
de données et stockées avec ses données.
Champs de Ils extraient des valeurs d’un ensemble de données spécifié lors de
référence l’exécution en fonction des critères de recherche que vous indiquez.
Agrégat Affiche une valeur de résumé des données contenues dans un ensemble
d’enregistrements.

Ces types de champs persistants ne servent que pour l’affichage. Les données
qu’ils contiennent au moment de l’exécution ne sont pas conservées, soit parce
qu’elles existent déjà ailleurs dans la base de données, soit parce qu’elles sont
temporaires. La structure physique de la table et des données sous-jacentes de
l’ensemble de données reste de toute façon inchangée.
Pour créer un nouveau composant champ persistant, cliquez avec le bouton droit
de la souris sur la boîte liste de l’éditeur de champs et choisissez Nouveau
champ. La boîte de dialogue Nouveau champ apparaît.
La boîte de dialogue Nouveau champ contient trois boîtes groupe : Propriétés du
champ, Type de champ et Définition de la référence.
La boîte groupe “Type de champ” vous permet de spécifier le type du nouveau
composant champ à créer. Son type par défaut est Données. Si vous choisissez
Référence, les boîtes de saisie Dataset et Champs clé de la boîte groupe
Définition de la référence sont activées. Il est aussi possible de créer des champs
calculés, et si vous travaillez avec un composant TClientDataSet, vous pouvez
même créer des champs CalcInterne.
La boîte groupe Propriétés du champ vous permet d’entrer des informations
générales sur le composant champ. Tapez le nom de champ du composant dans la
boîte de saisie Nom. Le nom que vous saisissez correspond à la propriété
FieldName du composant champ. C’est ce nom que Delphi utilise pour construire
un nom de composant dans la boîte de saisie Composant. Le nom qui s’y affiche
correspond à la propriété Name du composant champ ; il est fourni uniquement à
titre d’information (Name contient l’identificateur vous permettant de faire
référence au composant champ dans le code source). Delphi ne tient pas compte
de ce que vous entrez directement dans la boîte de saisie Composant.
La boîte à options “Type” vous permet de spécifier le type de données du
composant champ. Vous devez obligatoirement indiquer un type de données
pour chaque nouveau composant champ créé. Par exemple, pour afficher une
valeur monétaire à virgule flottante dans un champ, sélectionnez Currency dans
la liste déroulante. La boîte de saisie Taille vous permet de spécifier le nombre
maximum de caractères pouvant être affichés ou entrés dans un champ chaîne,
ou bien la taille des champs Bytes et VarBytes. Taille ne s’utilise pour aucun autre
type de données.
La boîte groupe Définition de la référence ne s’utilise que pour créer des champs
de référence. Pour plus de détails, reportez-vous à “Définition d’un champ de
référence” à la page 19-11.

19-8 Guide du développeur


Définition de nouveaux champs persistants

Définition d’un champ de données


Un champ de données remplace un champ existant dans un ensemble de
données. Il peut arriver que pour des raisons liées par exemple à la
programmation vous ayez à remplacer TSmallIntField par TIntegerField. Comme
vous ne pouvez pas changer directement son type de données, vous devez
définir un nouveau champ pour le remplacer.
Important Même si pour remplacer un champ existant vous en définissez un nouveau,
celui-ci doit dériver la valeur de ses données d’une colonne d’une table sous-
jacente d’un ensemble de données.
Pour créer un champ de données de remplacement d’un champ de la table sous-
jacente à un ensemble de données :
1 Retirez le champ de la liste des champs persistants affectée à l’ensemble de
données puis choisissez Nouveau champ dans le menu contextuel.
2 Dans la boîte de dialogue Nouveau champ, entrez dans la boîte de saisie Nom
le nom d’un champ existant de la table de la base de données. N’entrez pas
un nouveau nom de champ car vous spécifiez ici le nom d’un champ existant
dont le nouveau champ va dériver ses données .
3 Choisissez un nouveau type de données dans la boîte à options Type. Celui-ci
doit être différent de celui du champ que vous remplacez. Il n’est pas possible
de remplacer un champ chaîne d’une taille donnée par un champ chaîne
d’une autre taille. Même si le type de données doit être différent il doit
néanmoins être compatible avec le type de données réel du champ de la table
sous-jacente.
4 Le cas échéant, entrez la taille du champ dans la boîte de saisie
correspondante. Cela ne s’applique qu’aux champs de type TStringField,
TBytesField et TVarBytesField.
5 Sélectionnez Données dans la boîte groupe Type de champ.
6 Choisissez OK. La boîte de dialogue Nouveau champ se referme, le nouveau
champ remplace celui que vous avez spécifié à l’étape 1 et la déclaration de
composant dans le module de données ou dans la déclaration de type de la
fiche est mise à jour.
Pour éditer les propriétés ou les événements associés au composant champ,
sélectionnez le nom du composant dans la boîte liste de l’éditeur de champs,
puis éditez ses propriétés ou événements avec l’inspecteur d’objets. Pour plus de
détails sur l’édition des propriétés et des événements d’un composant champ,
reportez-vous à “Définition des événements et des propriétés des champs
persistants” à la page 19-14.

Manipulation des composants champ 19-9


Définition de nouveaux champs persistants

Définition d’un champ calculé


Un champ calculé, comme son nom l’indique, affiche les valeurs calculées lors de
l’exécution par le gestionnaire d’événement OnCalcFields d’un ensemble de
données. Par exemple, vous pouvez être amené à créer un champ chaîne qui
affiche des valeurs concaténées à partir d’autres champs.
Pour créer un champ calculé à partir de la boîte de dialogue Nouveau champ :
1 Saisissez un nom dans la boîte de saisie Nom (attention à ne pas entrer le
nom d’un champ existant).
2 Choisissez le type de données de ce champ dans la boîte à options Type.
3 Entrez la taille du champ dans la boîte de saisie correspondante, le cas
échéant. La taille ne s’applique qu’aux champs de type TStringField,
TBytesField et TVarBytesField.
4 Sélectionnez Calculé dans la boîte groupe Type de champ.
5 Choisissez OK. Le nouveau champ calculé est ajouté automatiquement à la fin
de la liste des champs persistants de la boîte liste de l’éditeur de champs et la
déclaration du composant est ajoutée automatiquement à la déclaration de
type de la fiche dans le code source.
6 Introduisez le code qui calcule les valeurs du champ dans le gestionnaire
d’événement OnCalcFields de l’ensemble de données. Pour savoir comment
écrire du code pour calculer la valeur des champs, voir “Programmation d’un
champ calculé” à la page 19-10.
Remarque Pour éditer les propriétés ou événements associés au composant champ,
sélectionnez le nom du composant dans la boîte liste de l’éditeur de champs,
puis procédez à l’édition avec l’inspecteur d’objets. Pour en savoir plus à ce
sujet, reportez-vous à “Définition des événements et des propriétés des champs
persistants” à la page 19-14.
Si vous travaillez avec un composant ensemble de données client ou requête,
vous pouvez aussi créer un champ CalcInterne. Ce dernier est créé et
programmé comme un champ calculé normal. Pour un ensemble de données
client, la principale différence entre les deux types de champs calculés est que les
valeurs calculées pour un champ CalcInterne sont stockées et extraites des
données de l’ensemble de données client. Pour créer un champ CalcInterne,
sélectionnez le bouton radio CalcInterne dans la boîte groupe Type de champ.

Programmation d’un champ calculé


Après avoir défini un champ calculé, vous devez écrire le code permettant d’en
calculer la valeur, sinon il aura toujours une valeur NULL. Le code d’un champ
calculé doit être placé dans l’événement OnCalcFields de son ensemble de
données.

19-10 Guide du développeur


Définition de nouveaux champs persistants

Pour programmer la valeur d’un champ calculé :


1 Sélectionnez le composant ensemble de données dans la liste déroulante de
l’inspecteur d’objets.
2 Choisissez la page Evénements dans l’inspecteur d’objets.
3 Double-cliquez sur la propriété OnCalcFields pour lancer ou créer une
procédure CalcFields pour le composant ensemble de données.
4 Ecrivez le code définissant les valeurs et les autres propriétés du champ
calculé.
Supposons par exemple que vous ayez créé un champ calculé CityStateZip pour
la table Customers dans le module de données CustomerData. CityStateZip
affichera le nom de la ville et de l’état où se trouve l’entreprise et le code postal
sur une même ligne d’un contrôle orienté données.
Pour ajouter du code à la procédure CalcFields pour la table Customers,
sélectionnez celle-ci dans la liste déroulante de l’inspecteur d’objets, passez à la
page Evénements et double-cliquez sur la propriété OnCalcFields.
La procédure TCustomerData.CustomersCalcFields apparaît dans la fenêtre de code
source de l’unité. Ajoutez le code suivant à la procédure pour calculer le champ :
CustomersCityStateZip.Value := CustomersCity.Value + ', ' + CustomersState.Value
+ ' ' + CustomersZip.Value;

Définition d’un champ de référence


Un champ de référence est un champ en lecture seule qui affiche des valeurs à
l’exécution en fonction du critère de recherche ayant été spécifié. Dans sa forme
la plus simple, on transmet à un champ de référence le nom du champ à
rechercher, la valeur à rechercher et le champ de l’ensemble de données de
référence dont la valeur doit s’afficher.
Prenons par exemple une application de VPC permettant à un opérateur
d’utiliser un champ de référence pour déterminer automatiquement la ville et
l’état correspondant à un code postal fourni par le client. Dans ce cas, la colonne
sur laquelle doit porter la recherche peut s’appeler ZipTable.Zip, la valeur à
rechercher étant le code postal du client tel qu’il a été entré dans Order.
CustZip et les valeurs à renvoyer étant celles des colonnes ZipTable.City et
ZipTable.State de l’enregistrement où ZipTable.Zip correspond à la valeur actuelle
du champ Order.CustZip.
Pour créer un champ de référence dans la boîte de dialogue Nouveau champ :
1 Saisissez le nom du champ de référence dans la boîte de saisie Nom.
Attention à ne pas saisir un nom de champ existant.
2 Dans la boîte à options Type, choisissez un type de données pour le champ.
3 Le cas échéant, entrez la taille du champ dans la boîte de saisie
correspondante. La taille n’est à spécifier que pour les champs de type
TStringField, TBytesField et TVarBytesField.

Manipulation des composants champ 19-11


Définition de nouveaux champs persistants

4 Sélectionnez Référence dans la boîte groupe Type de champ pour activer les
boîtes à options Dataset et Champs clé.
5 Choisissez dans la liste déroulante de la boîte à options “Dataset” l’ensemble
de données sur lequel doit porter la recherche des valeurs de champ.
L’ensemble de référence doit être différent de celui du composant champ lui-
même, sinon une exception de référence circulaire sera provoquée au moment
de l’exécution. En spécifiant un ensemble de données de référence, vous
activez les boîtes à options Clés de référence et Champ résultat.
6 Dans la liste déroulante Champs clé, choisissez dans l’ensemble de données
actif le champ dont les valeurs doivent se correspondre. Pour faire
correspondre plusieurs champs, entrez leur nom directement au lieu de les
choisir dans la liste déroulante. Séparez leur nom par des points-virgules. Si
vous utilisez plusieurs champs, il faut employer des composants champ
persistants.
7 Dans la liste déroulante Clés de référence, choisissez un champ de l’ensemble
de données de référence devant correspondre au champ source spécifié à
l’étape 6. Si vous avez spécifié plusieurs champs clé, il faut spécifier le même
nombre de clés de référence. Pour spécifier plusieurs champs, entrez leur nom
directement. Séparez leur nom par des points-virgules.
8 Dans la liste déroulante Champ résultat, choisissez un champ de l’ensemble
de données de référence à renvoyer comme valeur du champ de référence que
vous êtes en train de créer.
Lorsque vous concevez et exécutez votre application, les valeurs des champs de
référence sont déterminées avant celles des champs calculés. Il est possible de
créer des champs calculés basés sur des champs de référence mais il est
impossible de créer des champs de référence basés sur des champs calculés. Ce
comportement peut être affiné avec la propriété LookupCache Cette propriété
détermine si les valeurs d’un champ de référence sont placées en mémoire cache
lors de la première ouverture d’un ensemble de données ou si elles sont
référencées dynamiquement à chaque modification de l’enregistrement en cours
dans l’ensemble de données.
Mettez LookupCache à True pour cacher les valeurs d’un champ de référence
lorsque la propriété LookupDataSet ne risque aucune modification et que le
nombre de valeurs de référence est faible. La mise en mémoire cache des valeurs
de référence permet d’accélérer les performances, car les valeurs de référence
relatives à chaque ensemble de valeurs de la propriété LookupKeyFields sont
préchargées à l’ouverture de l’ensemble de données. En cas de modification de
l’enregistrement en cours dans l’ensemble de données, l’objet champ peut
localiser sa valeur (Value) dans la mémoire cache, plutôt que d’accéder à
LookupDataSet. Cette amélioration des performances est appréciable si l’ensemble
de données de référence (LookupDataSet) est sur un réseau présentant des temps
d’accès lents.

19-12 Guide du développeur


Définition de nouveaux champs persistants

Astuce Vous pouvez utiliser une mémoire cache de référence pour proposer les valeurs
de référence par programme au lieu d’utiliser un ensemble de données
secondaire. Créez un objet TLookupList à l’exécution et utilisez sa méthode Add
pour le remplir avec les valeurs de référence. Affectez cet objet TLookupList à la
propriété LookupList du champ de référence et affectez la valeur True à sa
propriété LookupCache. Si les autres propriétés de référence du champ ne sont pas
initialisées, le champ utilise la liste de référence spécifiée sans redéfinir ses
valeurs avec celles d’un ensemble de données de référence.
Si chaque enregistrement de l’ensemble de données (DataSet) comporte des
valeurs différentes pour KeyFields, le temps nécessaire à la localisation des
valeurs dans le cache peut être supérieur aux gains de temps obtenus grâce au
placement en mémoire cache. Plus le nombre de valeurs distinctes dans KeyFields
est élevé, plus le temps de traitement passé à localiser des valeurs dans la
mémoire cache augmente.
Si LookupDataSet risque de changer, le fait de placer des valeurs de référence en
mémoire cache peut provoquer des résultats erronés. Appelez RefreshLookupList
pour mettre à jour les valeurs dans la mémoire cache. RefreshLookupList régénère
la propriété LookupList qui contient la valeur de la propriété LookupResultField de
chaque ensemble de valeurs LookupKeyFields.
Si LookupCache est définie à l’exécution, appelez RefreshLookupList pour initialiser
la mémoire cache.

Définition d’un champ agrégat


Un champ agrégat affiche les valeurs issues d’un calcul portant sur un ensemble
de données client. Un agrégat est la somme des données contenues dans un
ensemble d’enregistrements.
Pour créer un champ agrégat dans la boîte de dialogue Nouveau champ :
1 Entrez un nom pour le champ agrégat dans la boîte de saisie Nom. N’entrez
pas le nom d’un champ existant.
2 Choisissez le type de données d’agrégat pour le champ dans la boîte à options
Type.
3 Sélectionnez l’option d’agrégat dans la boîte groupe Type de champ.
4 Choisissez OK. Le champ agrégat nouvellement défini est automatiquement
ajouté au composant Aggregates de l’ensemble de données client. Il est
automatiquement mis à jour pour inclure la spécification d’agrégat appropriée
et la déclaration du composant est automatiquement ajoutée à la déclaration
type de la fiche dans le code source.
5 Placez le calcul de la somme dans la propriété ExprText du champ agrégat
nouvellement créé. Pour plus d’informations sur la définition d’une somme,
voir “Représentation des valeurs calculées” à la page 24-10.

Manipulation des composants champ 19-13


Définition des événements et des propriétés des champs persistants

Une fois qu’un composant persistant TAggregateField est créé, un contrôle


TDBText peut être lié au champ agrégat. Le contrôle TDBText affiche alors la
valeur du champ agrégat en fonction de l’enregistrement en cours de l’ensemble
de données client sous-jacent.

Suppression de champs persistants


La suppression de champs persistants peut permettre d’accéder à un sous-
ensemble de colonnes d’une table et de définir vos propres champs persistants
afin de remplacer une colonne. Pour supprimer un ou plusieurs champs
persistants d’un ensemble de données :
1 Sélectionnez les champs à supprimer dans la boîte liste de l’éditeur de
champs.
2 Appuyez sur Suppr.
Remarque Vous pouvez également supprimer les champs sélectionnés en choisissant
Supprimer dans le menu contextuel de l’éditeur de champs.
Les champs supprimés ne sont plus disponibles pour l’ensemble de données et
ne peuvent plus être affichés par les contrôles orientés données. Vous avez
toujours la possibilité de recréer des composants champ persistants supprimés
par accident, mais les modifications préalablement apportées à leurs propriétés
ou événements seront perdues. Pour plus de détails, reportez-vous à “Création
de champs persistants” à la page 19-6.
Remarque Si vous supprimez tous les composants champ persistants d’un ensemble de
données, l’ensemble de données recommence à utiliser des composants champ
dynamique pour chaque colonne de la table de base de données sous-jacente.

Définition des événements et des propriétés des champs


persistants
Vous pouvez définir les propriétés et personnaliser les événements des
composants champ persistants lors de la conception. Les propriétés contrôlent la
façon dont un champ est affiché par un composant orienté données (elles
déterminent par exemple s’il doit apparaître dans un composant TDBGrid ou
bien si sa valeur peut être modifiée). Les événements contrôlent ce qui se passe
quand les données d’un champ sont extraites, modifiées, définies ou validées.
Pour définir les propriétés d’un composant champ ou écrire des gestionnaires
d’événements personnalisés pour celui-ci, sélectionnez-le dans l’éditeur de
champs ou dans la liste de l’inspecteur d’objets.

19-14 Guide du développeur


Définition des événements et des propriétés des champs persistants

Définition des propriétés d’affichage et d’édition en mode


conception
Pour éditer les propriétés d’affichage d’un composant champ sélectionné, accédez
à la page Propriétés de l’inspecteur d’objets. Le tableau suivant dresse la liste des
propriétés d’affichage pouvant être éditées.

Tableau 19.4 Propriétés du composant champ


Propriété Finalité
Alignment Aligne le contenu d’un champ à gauche, à droite ou au centre dans un
composant orienté données.
ConstraintErrorMessage Spécifie le texte à afficher en cas de condition de contrainte.
CustomConstraint Spécifie une contrainte locale à appliquer aux données lors de l’édition.
Currency Champs numériques seulement. True : affiche des valeurs monétaires.
False (par défaut) : n’affiche pas les valeurs monétaires.
DisplayFormat Spécifie le format d’affichage dans un composant orienté données.
DisplayLabel Spécifie le nom de colonne d’un champ dans un composant grille orienté
données.
DisplayWidth Spécifie la largeur, en caractères, d’une colonne de grille affichant le contenu
de ce champ.
EditFormat Spécifie le format d’édition dans un composant orienté données.
EditMask Limite l’entrée de données dans un champ de saisie aux types et aux
étendues de caractères spécifiés. Spécifie également les caractères spéciaux,
non éditables, qui apparaissent dans le champ (tirets, parenthèses, etc.).
FieldKind Spécifie le type du champ à créer.
FieldName Spécifie le nom réel d’une colonne de la table dont le champ dérive sa valeur
et son type de données.
HasConstraints Indique si des conditions de contrainte ont été imposées à un champ.
ImportedConstraint Spécifie une contrainte SQL importée du dictionnaire de données ou d’un
serveur SQL.
Index Spécifie la position du champ dans un ensemble de données.
LookupDataSet Spécifie la table utilisée pour référencer les valeurs de champs lorsque Lookup
est à True.
LookupKeyFields Spécifie le champ ou les champs de l’ensemble de données de référence
devant se correspondre lors d’un référencement.
LookupResultField Spécifie le champ de l’ensemble de données de référence dont la valeur doit
être copiée dans le champ de référence
MaxValue Champs numériques seulement. Spécifie la valeur maximum que l’utilisateur
peut entrer dans le champ.
MinValue Champs numériques seulement. Spécifie la valeur minimum que l’utilisateur
peut entrer dans le champ.
Name Spécifie le nom du composant champ dans Delphi.
Origin Spécifie le nom du champ tel qu’il apparaît dans la base de données sous-
jacente.

Manipulation des composants champ 19-15


Définition des événements et des propriétés des champs persistants

Tableau 19.4 Propriétés du composant champ (suite)


Propriété Finalité
Precision Champs numériques uniquement. Spécifie le nombre de chiffres significatifs.
ReadOnly True : affiche les valeurs de champ dans les composants orientés données,
mais en interdit l’édition.
False (par défaut) : autorise l’affichage et l’édition des valeurs du champ.
Size Spécifie le nombre maximum de caractères pouvant être affichés ou entrés
dans un champ chaîne, ou bien la taille en octets des champs TBytesField et
TVarBytesField.
Tag Indicateur d’entier long que le programmeur peut utiliser dans tous les
composants.
Transliterate True (par défaut) : spécifie qu’une traduction sera effectuée vers et depuis les
locales respectives au fur et à mesure que des données sont transférées entre
un ensemble de données et une base de données.
False : spécifie que ces traductions ne seront pas effectuées.
Visible True (valeur par défaut) : permet l’affichage du champ dans un composant
grille orienté données.
False : l’affichage du champ dans un composant grille orienté données.
Les composants définis par l’utilisateur peuvent afficher les décisions prises
en fonction de cette propriété.

Toutes les propriétés ne sont pas disponibles pour l’ensemble des composants
champ. Par exemple, un composant champ de type TStringField ne peut pas
avoir les propriétés Currency, MaxValue ou DisplayFormat et un composant de
type TFloatField ne peut pas avoir de propriété Size.
Alors que le rôle de la plupart des propriétés est évident, certaines comme
Calculated nécessitent des étapes de programmation supplémentaires. D’autres,
comme DisplayFormat, EditFormat et EditMask sont reliées entre elles ; leur
configuration doit être coordonnée. Pour plus de détails sur l’utilisation des
propriétés DisplayFormat, EditFormat et EditMask, voir “Contrôle ou dissimulation
de la saisie utilisateur” à la page 19-18.

Définition des propriétés des composants champ à l’exécution


Les propriétés des composants champ peuvent aussi être utilisées et manipulées
à l’exécution. Par exemple, le code ci-dessous donne la valeur True à la propriété
ReadOnly du champ CityStateZip de la table Customers :
CustomersCityStateZip.ReadOnly := True;
L’instruction suivante change l’ordre des champs en donnant la valeur 3 à la
propriété Index du champ CityStateZip de la table Customers :
CustomersCityStateZip.Index := 3;

19-16 Guide du développeur


Définition des événements et des propriétés des champs persistants

Création d’ensembles d’attributs pour les composants champ


Lorsque plusieurs champs des ensembles de données utilisés par votre
application partagent des propriétés de formatage (telles que Alignment,
DisplayWidth, DisplayFormat, EditFormat, MaxValue, MinValue, et ainsi de suite), il
est plus pratique de les définir pour un seul champ, puis de les stocker comme
ensemble d’attributs dans le dictionnaire des données. Ils peuvent alors être
appliqués facilement à d’autres champs.
Pour créer un ensemble d’attributs d’après un composant champ d’un ensemble
de données :
1 Double-cliquez sur l’ensemble de données pour lancer l’éditeur de champs.
2 Sélectionnez le champ dont vous voulez définir les propriétés.
3 Définissez les propriétés voulues pour ce champ dans l’inspecteur d’objets.
4 Cliquez avec le bouton droit de la souris sur la boîte liste de l’éditeur de
champs pour ouvrir le menu contextuel.
5 Choisissez Enregistrer les attributs pour enregistrer la propriété du champ en
cours comme ensemble d’attributs dans le dictionnaire de données.
Par défaut, l’ensemble d’attributs prend le nom du champ en cours. Vous
pouvez toutefois spécifier un nom différent en choisissant “Enregistrer les
attributs sous” au lieu de la commande “Enregistrer les attributs”.
Remarque Vous pouvez également créer des ensembles d’attributs directement depuis
l’explorateur SQL. Lorsque vous créez un ensemble d’attributs depuis le
dictionnaire de données, il ne peut s’appliquer à n’importe quel champ, mais
vous pouvez spécifier deux attributs supplémentaires : un type de champ (tel
que TFloatField, TStringField, etc.) et un contrôle orienté données (TDBEdit,
TDBCheckBox, etc.) placé automatiquement sur une fiche lorsque vous y faites
glisser un champ basé sur l’ensemble d’attributs. Pour en savoir plus à ce sujet,
reportez-vous à l’aide en ligne de l’explorateur SQL.

Association des ensembles d’attributs aux composants champ


Lorsque plusieurs champs des ensembles de données utilisés par votre
application partagent des propriétés de formatage (Alignment, DisplayWidth,
DisplayFormat, EditFormat, MaxValue, MinValue, etc.) et si vous avez enregistré ces
propriétés en tant qu’ensembles d’attributs dans le dictionnaire de données, vous
pouvez facilement appliquer ceux-ci aux champs sans avoir à les recréer
manuellement pour chacun d’eux. En outre, si vous changez ultérieurement la
configuration des attributs dans le dictionnaire de données, ces modifications
seront répercutées automatiquement à chaque champ associé, la prochaine fois
que des composants champ seront ajoutés à l’ensemble de données.
Pour attribuer un ensemble d’attributs à un composant champ :
1 Double-cliquez sur l’ensemble de données pour lancer l’éditeur de champs.
2 Sélectionnez le champ auquel l’ensemble d’attributs doit s’appliquer.

Manipulation des composants champ 19-17


Définition des événements et des propriétés des champs persistants

3 Cliquez avec le bouton droit de la souris sur la boîte liste et choisissez


Associer les attributs.
4 Sélectionnez ou entrez l’ensemble d’attributs à appliquer depuis la boîte de
dialogue Association d’attributs. Si un ensemble d’attributs porte ce nom dans
le dictionnaire de données, il apparaît dans la boîte de saisie.
Important Si l’ensemble d’attributs défini dans le dictionnaire de données est modifié par la
suite, vous devrez à nouveau l’appliquer à chaque composant champ qui
l’utilise. Vous pouvez appeler l’éditeur de champs pour sélectionner plusieurs
composants champ d’un ensemble de données.

Suppression des associations d’ensembles d’attributs


Si vous changez d’avis concernant l’association d’un ensemble d’attributs à un
champ, vous pouvez facilement supprimer l’association en procédant comme
suit :
1 Appelez l’éditeur de champs correspondant depuis l’ensemble de données
contenant le champ.
2 Sélectionnez le champ pour lequel vous voulez supprimer l’ensemble
d’attributs.
3 Appelez le menu contextuel de l’éditeur de champs et choisissez Dissocier les
attributs.
Important Le fait de dissocier un ensemble d’attributs ne modifie pas les propriétés du
champ. Le champ garde les paramètres qu’il avait lorsque l’ensemble d’attributs
lui a été affecté. Pour modifier ces propriétés, sélectionnez le champ dans
l’éditeur de champs et définissez les propriétés dans l’inspecteur d’objets.

Contrôle ou dissimulation de la saisie utilisateur


La propriété EditMask permet de contrôler le type et la portée des valeurs qu’un
utilisateur peut entrer dans un composant orienté données associé à des
composants TStringField, TDateField, TTimeField et TDateTimeField. Vous pouvez
soit utiliser des masques existants ou créer les vôtres. La manière la plus simple
consiste à faire appel à l’éditeur de masques de saisie. Cependant, vous avez
aussi la possibilité d’entrer les masques directement dans le champ EditMask de
l’inspecteur d’objets.
Remarque Pour les composants TStringField, la propriété EditMask définit aussi le format
d’affichage.
Pour entrer un masque de saisie, appelez l’éditeur de masques de saisie d’un
composant champ :
1 Sélectionnez le composant dans l’éditeur de champs ou dans l’inspecteur
d’objets.
2 Cliquez sur la page Propriétés de l’inspecteur d’objets.

19-18 Guide du développeur


Définition des événements et des propriétés des champs persistants

3 Double-cliquez sur la colonne de la propriété EditMask dans l’inspecteur


d’objets ou cliquez sur le bouton à points de suspension. L’éditeur de
masques de saisie s’ouvre.
La boîte de saisie “Masque” vous permet de créer et d’éditer un format de
masque. La grille Masques exemple vous permet de sélectionner des masques
prédéfinis. Si vous en sélectionnez un, son format apparaît dans la boîte de saisie
et vous pouvez alors soit le modifier, soit l’utiliser tel quel. La boîte Test de
saisie vous permet également de tester les entrées utilisateur.
Le bouton Masques vous permet de charger un ensemble de masques
personnalisé (si vous en avez créé un) dans la grille Masques exemple.

Utilisation des formats par défaut pour les champs numériques,


date et heure
Delphi fournit des routines intégrées d’affichage et d’édition ainsi qu’un
formatage par défaut intelligent pour les composants TFloatField, TCurrencyField,
TIntegerField, TSmallIntField, TWordField, TDateField, TDateTimeField et TTimeField.
Vous n’avez donc rien à faire pour utiliser ces routines.
Le formatage par défaut est effectué par les routines suivantes :

Tableau 19.5 Routines de formatage des composants champ


Routine Utilisée par . . .
FormatFloat TFloatField, TCurrencyField
FormatDateTime TDateField, TTimeField, TDateTimeField
FormatCurr TCurrencyField

Seules les propriétés de format appropriées au type de données d’un composant


champ sont disponibles pour un composant donné.
Les conventions de format par défaut des valeurs de date, d’heure, monétaire et
numériques reposent sur les propriétés Paramètres régionaux du Panneau de
configuration. Par exemple, si vous utilisez les paramètres par défaut des Etats-
Unis, la colonne TFloatField dont la propriété Currency est à True remplace la
valeur 1234,56 de la propriété DisplayFormat par $1234.56, tandis que la valeur de
EditFormat devient 1234.56.
Au moment de la conception ou de l’exécution, vous pouvez éditer les propriétés
DisplayFormat et EditFormat d’un composant champ pour ignorer la configuration
d’affichage par défaut de ce champ. Vous pouvez également écrire des
gestionnaires d’événements OnGetText et OnSetText pour effectuer un formatage
personnalisé des composants champ lors de l’exécution. Pour en savoir plus sur
leur définition, reportez-vous à “Définition des propriétés des composants champ
à l’exécution” à la page 19-16.

Manipulation des composants champ 19-19


Manipulation des méthodes de champ lors de l’exécution

Gestion des événements


A l’instar de tous les composants Delphi, les composants champ sont associés à
des gestionnaires d’événements. Ceux-ci vous permettent de contrôler les
événements affectant les données saisies dans les champs au moyen de contrôles
orientés données. Le tableau suivant dresse la liste des événements associés aux
composants champ :
Tableau 19.6 Evénements des composants champ
Evénement Finalité
OnChange Appelé lors du changement de la valeur d’un champ.
OnGetText Appelé lors de l’extraction de la valeur d’un composant champ pour
affichage ou édition.
OnSetText Appelé lors de la définition de la valeur d’un composant champ.
OnValidate Appelé pour valider la valeur d’un composant champ à chaque fois qu’elle
est modifiée suite à une édition ou une insertion.

Les événements OnGetText et OnSetText sont surtout utiles aux programmeurs


qui veulent aller au-delà des fonctions de formatage intégrées. OnChange permet
d’effectuer des tâches spécifiques à une application et associées aux modifications
de données, comme l’activation ou la désactivation de menus ou de contrôles
visuels. OnValidate est utile pour valider la saisie de données dans votre
application avant de renvoyer les valeurs à un serveur de base de données.
Pour écrire un gestionnaire d’événement pour un composant champ :
1 Sélectionnez le composant.
2 Sélectionnez la page Evénements dans l’inspecteur d’objets.
3 Double-cliquez sur la colonne des valeurs du gestionnaire d’événement pour
ouvrir la fenêtre de code source correspondante.
4 Créez ou éditez le code du gestionnaire.

Manipulation des méthodes de champ lors de l’exécution


Les méthodes des composants champ disponibles au moment de l’exécution vous
permettent de convertir les valeurs des champs d’un type en un autre et de
placer la focalisation sur le premier contrôle orienté données d’une fiche associée
à un composant champ.
Le contrôle de la focalisation des composants orientés données associés à un
champ est important quand votre application effectue la validation de données
orientées enregistrement dans un gestionnaire d’événement d’ensemble de
données (comme BeforePost). Il est possible de procéder à la validation des champs
d’un enregistrement, que son contrôle orienté données associé ait la focalisation
ou non. Si la validation échoue pour un champ particulier de l’enregistrement,
vous devez faire en sorte que le contrôle contenant les données à l’origine de
l’erreur reçoive la focalisation afin que l’utilisateur puisse entrer les corrections.

19-20 Guide du développeur


Affichage, conversion et accès aux valeurs des champs

Pour contrôler la focalisation des composants orientés données d’un champ, vous
devez recourir à la méthode FocusControl. Celle-ci place la focalisation sur le
premier contrôle orienté données d’une fiche associé à un champ. Un
gestionnaire d’événement doit faire appel à la méthode FocusControl d’un champ
avant de le valider. Le code ci-dessous montre comment faire appel à la méthode
FocusControl pour le champ Company de la table Customers :
CustomersCompany.FocusControl;
Le tableau suivant dresse la liste des autres méthodes des composants champ et
présente leur utilisation. Pour une liste complète des méthodes et des
informations détaillées sur leur utilisation, voir les entrées relatives à TField et
ses descendants dans le Guide de référence de la bibliothèque de composants visuels.
Tableau 19.7 Méthodes des composants champ
Méthode Utilisation
AssignValue Affecte à une valeur de champ une valeur spécifiée en utilisant une
fonction de conversion basée sur le type du champ.
Clear Efface le champ et met sa valeur à NULL.
GetData Extrait du champ des données non formatées.
IsValidChar Détermine si un caractère saisi par un utilisateur dans un contrôle orienté
données dans le but de définir une valeur est autorisé.
SetData Affecte des données “brutes” au champ.

Affichage, conversion et accès aux valeurs des champs


Les contrôles orientés données comme TDBEdit et TDBGrid affichent
automatiquement les valeurs associées aux composants champ. Si l’édition est
activée pour l’ensemble de données et les contrôles, les contrôles orientés données
peuvent aussi envoyer des valeurs nouvelles et modifiées dans la base de
données. En général, les propriétés et les méthodes incorporées des contrôles
orientés données leur permettent de se connecter à des ensembles de données,
d’afficher des valeurs et d’effectuer des mises à jour sans que cela requière de
programmation supplémentaire de votre part. Utilisez-les autant que possible
dans vos applications de bases de données. Pour plus d’informations sur les
contrôles orientés données, voir chapitre 26, “Utilisation de contrôles de données”.
Les contrôles standard peuvent aussi afficher et éditer des valeurs de base de
données associées à des composants champ. L’utilisation de contrôles standard
peut toutefois nécessiter un supplément de programmation de votre part.

Affichage de valeurs dans les contrôles standard


Une application peut accéder à la valeur d’une colonne de base de données au
moyen de la propriété Value d’un composant champ. L’instruction ci-dessous, par
exemple, affecte la valeur du champ CustomersCompany au texte d’un contrôle
TEdit :
Edit3.Text := CustomersCompany.Value;

Manipulation des composants champ 19-21


Affichage, conversion et accès aux valeurs des champs

Cette méthode fonctionne bien pour les valeurs chaîne, mais elle peut nécessiter
un surcroît de programmation pour pouvoir gérer les conversions d’autres types
de données. Heureusement, les composants champ ont des fonctions intégrées
leur permettant de le faire.
Remarque Vous pouvez également utiliser des variants pour accéder aux valeurs des
champs et les définir. Les variants sont un nouveau type de données d’une
grande souplesse. Pour plus de détails sur leur utilisation pour accéder aux
valeurs de champ et les définir, reportez-vous à “Accès à des valeurs par la
propriété d’ensemble de données par défaut” à la page 19-23.

Conversion des valeurs de champs


Le rôle de ces fonctions est de convertir un type de données en un autre. Par
exemple, la fonction AsString convertit les valeurs numériques et booléennes en
représentations chaîne. Le tableau suivant donne la liste des fonctions de
conversion des composants champ et indique celles conseillées en fonction de
leur type :

Tableau 19.8 Fonctions de conversion des composants champ


TDateTimeField
TCurrencyField

TVarBytesField
TSmallintField

TGraphicField
TBooleanField
TIntegerField
TStringField

TMemoField
TBytesField
TWordField

TFloatField

TTimeField

TBlobField
TDateField
TBCDField

Fonction
AsVariant ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓
AsString ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓
AsInteger ✓ ✓ ✓ ✓
AsFloat ✓ ✓ ✓ ✓ ✓ ✓ ✓
AsCurrency ✓ ✓ ✓ ✓ ✓ ✓ ✓
AsDateTime ✓
AsBoolean ✓

Vous remarquerez qu’il est conseillé d’utiliser la méthode AsVariant pour


traduire tous les types de données. Utilisez-la en cas de doute.
Il est des cas où la conversion n’est pas possible. Par exemple, AsDateTime
permet de convertir une chaîne au format date, heure ou date/heure seulement
si la valeur de la chaîne est dans un format date/heure identifiable. L’échec
d’une tentative de conversion a pour effet de provoquer une exception.
Dans certains autres cas, la conversion est possible, mais les résultats sont parfois
imprévisibles. Par exemple, que signifie la conversion d’une valeur
TDateTimeField au format flottant ? AsFloat convertit la partie date du champ en
nombre de jours à partir du 31/12/1899 et la partie heure du champ en une

19-22 Guide du développeur


Affichage, conversion et accès aux valeurs des champs

fraction de 24 heures. Le tableau 19.9 donne la liste des conversions autorisées


qui donnent des résultats spéciaux :

Tableau 19.9 Résultats de conversion spéciaux


Conversion Résultat
De String à Boolean Convertit “True,” “False,” “Yes” et “No” en valeur booléenne. Les
autres valeurs provoquent une exception.
De Float à Integer Arrondit la valeur à virgule flottante à l’entier le plus proche.
De DateTime à Float Convertit la date en nombre de jours à partir du 31 décembre 1899 et
l’heure en fraction de 24 heures.
De Boolean à String Convertit toute valeur booléenne en “True” ou “False”.

Dans les autres cas, la conversion est totalement impossible, toute tentative
provoquant une exception.
Vous pouvez utiliser une fonction de conversion comme n’importe quelle
méthode appartenant à un composant : ajoutez le nom de la fonction à la fin du
nom du composant lorsqu’elle apparaît dans une instruction d’affectation. La
conversion a toujours lieu avant l’affectation. Par exemple, l’instruction ci-
dessous convertit la valeur de CustomersCustNo en chaîne et affecte celle-ci au
texte d’un contrôle de saisie :
Edit1.Text := CustomersCustNo.AsString;
A l’inverse, l’instruction suivante affecte le texte d’un contrôle de saisie au
champ CustomersCustNo en tant qu’entier :
MyTableMyField.AsInteger := StrToInt(Edit1.Text);
Si une conversion non supportée est effectuée lors de l’exécution, cela a pour
effet de provoquer une exception.

Accès à des valeurs par la propriété d’ensemble de données par


défaut
La meilleure méthode pour accéder à la valeur d’un champ consiste à recourir à
la propriété FieldValues. Par exemple, l’instruction suivante prend la valeur d’une
boîte de saisie et la transfère dans le champ CustNo de la table Customers :
Customers.FieldValues['CustNo'] := Edit2.Text;
Pour en savoir plus sur les variants, voir l’aide en ligne.

Accès à des valeurs par la propriété Fields d’un ensemble de


données
Vous pouvez accéder à la valeur d’un champ par la propriété Fields du
composant ensemble de données auquel appartient le champ. C’est utile si vous
devez parcourir un certain nombre de colonnes ou si votre application utilise des
tables non disponibles lors de la conception.

Manipulation des composants champ 19-23


Affichage, conversion et accès aux valeurs des champs

Pour utiliser la propriété Fields, vous devez connaître l’ordre et le type de


données d’un champ de l’ensemble de données. Pour spécifier le champ auquel
vous voulez accéder, vous devez utiliser un nombre ordinal. Le premier champ
d’un ensemble de données porte le numéro 0. La valeur des champs doit être
convertie correctement à l’aide de la routine de conversion du composant champ.
Pour plus de détails sur les fonctions de conversion, reportez-vous à “Conversion
des valeurs de champs” à la page 19-22.
Par exemple, l’instruction ci-dessous affecte la valeur actuelle de la septième
colonne (pays) de la table Customers à un contrôle de saisie :
Edit1.Text := CustTable.Fields[6].AsString;
A l’inverse, vous pouvez affecter une valeur à un champ en définissant la
propriété Fields de l’ensemble de données pour le champ voulu. Exemple :
begin
Customers.Edit;
Customers.Fields[6].AsString := Edit1.Text;
Customers.Post;
end;

Accès à des valeurs par la méthode FieldByName d’un ensemble


de données
Vous pouvez également accéder à la valeur d’un champ par la méthode
FieldByName d’un ensemble de données. Cette méthode est utile si vous
connaissez le nom du champ auquel vous voulez accéder, mais si vous n’avez
pas accès à la table sous-jacente au moment de la conception.
Pour utiliser FieldByName, vous devez connaître l’ensemble de données et le nom
du champ auxquels vous voulez accéder pour transmettre le nom du champ à la
méthode en tant qu’argument. Pour accéder à la valeur du champ ou la
modifier, vous devez convertir le résultat avec la fonction de conversion de
composant champ appropriée, comme par exemple AsString ou AsInteger. Par
exemple, l’instruction suivante affecte la valeur du champ CustNo de l’ensemble
de données Customers à un contrôle de saisie :
Edit2.Text := Customers.FieldByName('CustNo').AsString;
A l’inverse, vous pouvez affecter une valeur à un champ :
begin
Customers.Edit;
Customers.FieldByName('CustNo').AsString := Edit2.Text;
Customers.Post;
end;

19-24 Guide du développeur


Vérification de la valeur en cours d’un champ

Vérification de la valeur en cours d’un champ


Si votre application utilise un composant TClientDataSet ou gère un ensemble de
données servant de source de données à un composant TProvider sur un serveur
d’applications et que vous rencontrez des difficultés lors de la mise à jour des
enregistrements, vous pouvez utiliser la propriété CurValue pour examiner la
valeur de l’enregistrement à l’origine du problème. CurValue renvoie la valeur en
cours du composant champ et reflète les modifications apportées par d’autres
utilisateurs de la base de données.
La propriété CurValue permet d’examiner la valeur d’un champ si un problème
survient lorsqu’une valeur est émise dans la base de données. Une erreur
OnReconcileError se produit dès que la valeur émise pose un problème tel qu’une
violation de clé. Dans un gestionnaire d’événement OnReconcileError, NewValue
représente la valeur non validée à l’origine du problème, OldValue est la valeur
initialement affectée au champ et CurValue est la valeur actuellement affectée au
champ. CurValue peut être différente de OldValue si un autre utilisateur a
modifié la valeur du champ après la lecture de OldValue.

Définition de la valeur par défaut d’un champ


Vous pouvez spécifier la façon dont la valeur d’un champ doit être calculée à
l’exécution en utilisant la propriété DefaultExpression. Cette propriété peut être
l’expression d’une valeur SQL valide qui ne fait pas référence à des valeurs de
champs. Si l’expression contient des valeurs littérales autres que les valeurs
numériques, elles doivent être entourées de guillemets. Par exemple, si un champ
heure contient une valeur par défaut équivalente à midi, elle apparaît ainsi (les
guillemets doivent entourer la valeur littérale) :
‘12:00:00’

Utilisation de contraintes
Les composants champ peuvent utiliser des contraintes de serveur SQL. De plus,
vos applications peuvent créer et utiliser des contraintes personnalisées locales à
l’application. Toutes les contraintes sont des règles ou des conditions qui
définissent et imposent des limites sur un intervalle ou une portée de valeurs
stockées dans un champ. Les sections suivantes décrivent comment les
contraintes peuvent être utilisées avec les composants champ.

Création d’une contrainte personnalisée


A la différence des autres contraintes, une contrainte personnalisée n’est pas
importée du serveur. C’est une contrainte que vous déclarez et implémentez dans
votre application locale. C’est pourquoi les contraintes personnalisées peuvent être
utiles pour rendre obligatoire la pré-validation de la saisie de données, mais ne
peuvent être appliquées sur des données reçues d’un serveur d’applications ou
envoyées au serveur.

Manipulation des composants champ 19-25


Utilisation de contraintes

Pour créer une contrainte personnalisée, définissez la propriété CustomConstraint


de façon à ce qu’elle spécifie une condition de contrainte et affectez à la
propriété ConstraintErrorMessage le texte du message affiché lorsqu’un utilisateur
effectue une violation de contrainte à l’exécution.
CustomConstraint est une chaîne SQL qui spécifie une contrainte propre à
l’application et imposée à la valeur du champ. Définissez CustomConstraint de
façon à limiter les valeurs pouvant être saisies par un utilisateur dans un champ.
CustomConstraint peut être toute expression de recherche SQL valide telle que :
x > 0 and x < 100
Le nom utilisé pour faire référence à la valeur du champ peut être toute chaîne
autre qu’un mot SQL réservé, tant qu’elle est utilisée avec homogénéité à
l’intérieur de l’expression de contrainte.
Les contraintes personnalisées sont imposées en plus d’éventuelles contraintes
provenant du serveur. Pour voir quelles sont les contraintes imposées par le
serveur, examinez la propriété ImportedConstraint.

Utilisation des contraintes de serveur


La plupart des bases de données SQL d’exploitation utilisent les contraintes pour
imposer des conditions sur les valeurs possibles d’un champ. Par exemple, un
champ peut ne pas autoriser de valeurs NULL, peut requérir qu’une valeur soit
unique pour une colonne et que ces valeurs soient comprises entre 0 et 150.
Alors que ces conditions pourraient être répliquées dans vos applications client,
la propriété ImportedConstraint vous permet de diffuser localement les contraintes
d’un serveur.
ImportedConstraint est une propriété en lecture seule qui spécifie une clause SQL
limitant les valeurs de champ de la même manière. Par exemple :
Value > 0 and Value < 100
Ne modifiez pas la valeur de ImportedConstraint, sauf si vous voulez modifier des
commandes SQL non standard ou propres à un serveur qui ont été importées en
tant que commentaires, car le moteur de bases de données n’a pas pu les
interpréter.
Pour ajouter des contraintes supplémentaires sur la valeur d’un champ, utilisez
la propriété CustomConstraint. Les contraintes personnalisées sont imposées en
plus des contraintes importées. Si les contraintes du serveur changent, la valeur
de ImportedConstraint change aussi, mais les contraintes introduites dans la
propriété CustomConstraint persistent.
La suppression des contraintes de la propriété ImportedConstraint ne modifie pas
la validité des valeurs de champs qui sont en violation avec ces contraintes. La
suppression des contraintes provoque leur vérification par le serveur (la
vérification est sinon effectuée localement). Lorsque les contraintes sont vérifiées
localement, le message d’erreur spécifié dans la propriété ConstraintErrorMessage
apparaît en cas de violation (sinon, les messages d’erreur affichés proviennent du
serveur).

19-26 Guide du développeur


Utilisation des champs objet

Utilisation des champs objet


Les descendants de champs objet (TObjectField) acceptent les types de champ
ADT (Abstract Data Type), tableau, ensemble et référence. Ces types de champ
contiennent des champs enfants ou d’autres ensembles de données ou y font
référence. Un champ ADT contient des champs enfants qui peuvent être de
n’importe quel type scalaire ou un type d’objet. Un champ tableau contient un
tableau de champs enfants de type identique.
Les champs ensemble de données et de référence correspondent à des champs
qui accèdent à d’autres ensembles de données. Un champ ensemble de données
permet d’accéder à un ensemble de données imbriqué. Un champ de référence
stocke un pointeur (référence) sur un autre objet persistant (ADT).

Tableau 19.10 Types de composants champ objet


Nom du composant Utilisation
TADTField Représente un champ ADT (Abstract Data Type).
TArrayField Représente un champ tableau.
TDataSetField Représente un champ contenant une référence à un ensemble de
données imbriqué.
TReferenceField Représente un champ REF, pointant sur un champ ADT.

Quand vous ajoutez des champs avec l’éditeur de champs à un ensemble de


données contenant des champs objet, des champs objet persistants du type
correct sont automatiquement créés pour vous. Lorsque vous ajoutez des champs
objet persistants à un ensemble de données, la propriété ObjectView de
l’ensemble de données prend automatiquement la valeur True, et les champs sont
donc stockés de façon hiérarchisée.
Les propriétés suivantes sont communes à tous les descendants de champs objet
et permettent de gérer les ensembles de données et les champs enfants.

Tableau 19.11 Propriétés communes des descendants de champs objet


Propriété Utilisation
Fields Contient les champs enfants du champ objet.
ObjectType Classification du champ objet.
FieldCount Nombre de champs enfants appartenant au champ objet.
FieldValues Permet d’accéder aux valeurs des champs enfants du champ
objet.

Affichage des champs ADT et tableau


Les champs ADT et tableau contiennent des champs enfants qui peuvent être
affichés par le biais de contrôles orientés données. Les contrôles orientés données
tels que TDBEdit et TDBGrid affichent automatiquement les types de champ ADT
et tableau.

Manipulation des composants champ 19-27


Utilisation des champs objet

Les contrôles orientés données dotés d’une propriété DataField affichent


automatiquement les champs ADT et tableau et leurs champs enfants dans la
liste déroulante. Lorsqu’un champ ADT ou tableau est lié à un contrôle orienté
données, les champs enfants apparaissent dans le contrôle, dans une chaîne non
modifiable, séparés par des virgules. Un champ enfant est lié au contrôle en tant
que champ de données normal.
Un contrôle TDBGrid affiche différemment les données de champs ADT et
tableau, suivant la valeur de la propriété ObjectView de l’ensemble de données.
Lorsque ObjectView vaut False, chaque champ enfant apparaît dans une seule
colonne. Lorsque ObjectView vaut True, un champ ADT ou tableau peut être
développé et réduit en cliquant sur la flèche dans la barre de titre de la colonne.
Lorsque le champ est développé, chaque champ enfant apparaît dans sa propre
colonne et barre de titre, et tous sous la barre de titre du champ ADT ou
tableau. Lorsque le champ ADT ou tableau est réduit, seule une colonne
apparaît, contenant une chaîne non modifiable des champs enfants, séparés par
des virgules.

Utilisation des champs ADT


Les types de champs ADT sont définis par l’utilisateur et créés sur le serveur. Ils
s’apparentent aux structures. Un type de champ ADT peut contenir la plupart
des champ scalaires, des champs tableau, des champs de référence et des champs
ADT imbriqués.

Accès aux valeurs de champ ADT


Il existe plusieurs façons d’accéder aux données des types de champ ADT. La
création et l’utilisation de champs persistants sont fortement recommandées. Les
exemples suivants affectent une valeur de champ enfant à une boîte de saisie
appelée CityEdit. Ils utilisent la structure ADT suivante.
Address
Street
City
State
Zip
...et les champs persistants suivants créés pour le composant table Customer,
CustomerAddress: TADTField;
CustomerAddrStreet: TStringField;
CustomerAddrCity: TStringField;
CustomerAddrState: TStringField;
CustomerAddrZip: TStringField;
La ligne de code suivante utilise un champ persistant et illustre la meilleure
façon d’accéder aux données de champs ADT.
CityEdit.Text := CustomerAddrCity.AsString;

19-28 Guide du développeur


Utilisation des champs objet

Les exemples de code suivants exigent que la propriété ObjectView de l’ensemble


de données vaille True pour pouvoir être compilés. Ils ne requièrent pas de
champs persistants.
L’exemple suivant utilise un nom entièrement qualifié avec la méthode
FieldByName sur l’ensemble de données.
CityEdit.Text := Customer.FieldByName(‘Address.City’).AsString;
Vous pouvez accéder à la valeur d’un champ enfant avec la propriété FieldValues
de TADTField. FieldValues accepte et renvoie un Variant ; elle peut dont traiter et
convertir des champs de n’importe quel type. C’est également la propriété par
défaut de TObjectField qui peut donc être omise. Le paramètre d’index accepte
une valeur entière qui spécifie le décalage du champ. Par exemple,
CityEdit.Text := TADTField(Customer.FieldByName('Address'))[1];
qui est la même chose que
CityEdit.Text := TAdtField(Customer.FieldByName('Address')).FieldValues[1];
Le code suivant utilise la propriété Fields du composant TADTField.
CityEdit.Text := TADTField(Customer.FieldByName(‘Address’)).Fields[1].AsString;
Le code suivant utilise la propriété Fields du composant TADTField et la
propriété FieldByName de l’ensemble de données et de l’objet TFields.
CityEdit.Text :=
TADTField(Customer.FieldByName(‘Address’)).Fields.FieldByName(‘City’).AsString;
Comme vous pouvez le constater dans le dernier exemple, l’accès aux données
du champ par le biais de champs persistants est beaucoup plus simple. Les
méthodes d’accès supplémentaires sont surtout utiles lorsque la structure de la
table de la base de données n’est pas définitive ou connue lors de la conception.
Les valeurs de champ ADT sont aussi accessibles par le biais de la propriété
FieldValues d’un ensemble de données :
Customer.Edit;
Customer['Address.City'] := CityEdit.Text;
Customer.Post;
L’instruction suivante lit une valeur de chaîne à partir du champ enfant City du
champ ADT Address et la restitue dans une boîte de saisie :
CityEdit.Text := Customer['Address.City'];
Remarque La propriété ObjectView de l’ensemble de données peut valoir True ou False pour
que ces lignes de code puissent être compilées.

Utilisation des champs tableau


Les champs tableau se composent d’un ensemble de champs de même type. Les
types de champ peuvent être scalaires (par exemple à virgule flottante, chaîne)
ou non scalaires (ADT), mais un champ tableau de tableaux n’est pas autorisé.
La propriété SparseArrays de TDataSet détermine si un objet unique TField est
créé pour chaque élément du champ tableau.

Manipulation des composants champ 19-29


Utilisation des champs objet

Accès aux valeurs de champs tableau


Il existe plusieurs façons d’accéder aux données contenues dans les types de
champs tableau. L’exemple suivant remplit une boîte liste avec tous les éléments
de tableau n’ayant pas NULL pour valeur.
var
OrderDates: TArrayField;
I: Integer;
begin
for I := 0 to OrderDates.Size - 1 do
begin
if OrderDates.Fields[I].IsNull then Break;
OrderDateListBox.Items.Add(OrderDates[I]);
end;
end;
Les exemples suivants affectent une valeur de champ enfant à une boîte de saisie
appelée TelEdit et utilisent le tableau TelNos_Array, composé de six chaînes. Ils
utilisent les champs persistants suivants créés pour le composant table Customer :
CustomerTelNos_Array: TArrayField;
CustomerTelNos_Array0: TStringField;
CustomerTelNos_Array1: TStringField;
CustomerTelNos_Array2: TStringField;
CustomerTelNos_Array3: TStringField;
CustomerTelNos_Array4: TStringField;
CustomerTelNos_Array5: TStringField;
La ligne de code suivante utilise un champ persistant pour affecter une valeur
d’élément de tableau à une boîte de saisie.
TelEdit.Text := CustomerTelNos_Array0.AsString;
Les exemples de code suivants exigent que la propriété ObjectView de l’ensemble
de données vaille True pour pouvoir être compilés. Ils ne requièrent pas de
champs persistants.
Vous pouvez accéder à la valeur d’un champ enfant avec la propriété FieldValues
de l’ensemble de données. FieldValues accepte et renvoie un Variant ; elle peut
dont traiter et convertir des champs de n’importe quel type. Par exemple,
TelEdit.Text := TArrayField(Customer.FieldByName('TelNos_Array'))[1];
qui est la même chose que
TelEdit.Text := TArrayField(Table1.FieldByName('TelNos_Array')).FieldValues[1];
Le code suivant utilise la propriété Fields du composant TArrayField.
TelEdit.Text := TArrayField(Customer.FieldByName(‘TelNos_Array’)).Fields[1].AsString;

Utilisation des champs ensemble de données


Les champs ensemble de données permettent d’accéder aux données stockées
dans un ensemble de données imbriqué. La propriété NestedDataSet contient des
références à l’ensemble de données imbriqué. Les données contenues dans
l’ensemble de données imbriqué sont accessibles par le biais des objets champ de
cet ensemble de données.

19-30 Guide du développeur


Utilisation des champs objet

Affichage des champs ensemble de données


Les contrôles TDBGrid permettent l’affichage des données stockées dans les
champs ensemble de données. Dans un contrôle TDBGrid, un champ ensemble
de données est désigné dans chaque cellule de la colonne de l’ensemble de
données avec (DataSet) et, à l’exécution, avec un bouton points de suspension
sur la droite. Le fait de cliquer sur ce bouton appelle une nouvelle fiche dont la
grille affiche l’ensemble de données associé au champ ensemble de données de
l’enregistrement en cours. Cette fiche peut aussi être appelée par programmation
avec la méthode ShowPopupEditor. Par exemple, si la septième colonne de la
grille représente un champ ensemble de données, le code suivant affiche
l’ensemble de données associé à ce champ pour l’enregistrement en cours.
DBGrid1.ShowPopupEditor(DBGrid1.Columns[7]);

Accès aux données d’un ensemble de données imbriqué


Normalement un champ ensemble de données n’est pas directement lié à un
contrôle orienté données. En fait, comme un champ ensemble de données est
avant tout un ensemble de données, cela signifie qu’on accède à ses données via
un descendant de TDataSet. Ce descendant particulier de TDataSet est
TNestedTable, il propose les fonctionnalités spécifiques nécessaires à l’accès aux
données contenues dans des ensembles de données imbriqués. Une fois un
composant TDataSetField associé avec un champ ensemble de données, il est
possible de créer des champs persistants pour les champs de l’ensemble de
données imbriqué.
Pour accéder aux données d’un champ ensemble de données, vous devez
d’abord créer un objet TDataSetField persistant en appelant l’éditeur de champ de
la table puis pointer sur ce champ à l’aide de la propriété DatasetField d’un objet
TNestedTable ou TClientDataSet. Si le champ ensemble de données imbriqué est
attribué, l’ensemble de données imbriqué contient des enregistrements avec les
données référencées, sinon l’ensemble de données imbriqué est vide.
Avant d’insérer un enregistrement dans un ensemble de données imbriqué, vous
devez vous assurer de valider l’enregistrement correspondant dans la table
maître, s’il vient d’y être inséré. Si l’enregistrement inséré n’est pas validé, il est
automatiquement validé avant l’ensemble de données imbriqué.

Utilisation de champs de référence


Les champs de référence stockent un pointeur ou une référence vers un autre
objet ADT. Cet objet ADT est un enregistrement unique d’une autre table objet.
Les champs de référence font toujours référence à un enregistrement unique d’un
ensemble de données (table objet). Les données contenues dans l’objet référencé
sont effectivement renvoyées dans un ensemble de données imbriqué mais sont
aussi accessibles via la propriété Fields sur le composant TReferenceField.

Manipulation des composants champ 19-31


Utilisation des champs objet

Affichage des champs de référence


Dans un contrôle TDBGrid, un champ de référence est désigné dans chaque
cellule de la colonne de l’ensemble de données avec (Reference) et, à l’exécution,
avec un bouton à points de suspension sur la droite. A l’exécution, le fait de
cliquer sur ce bouton appelle une nouvelle fiche dont la grille affiche l’objet
associé au champ de référence de l’enregistrement en cours.
Cette fiche peut aussi être appelée par programmation avec la méthode
ShowPopupEditor de la grille DB. Par exemple, si la septième colonne de la grille
représente un champ de référence, le code suivant affiche l’objet associé à ce
champ pour l’enregistrement en cours.
DBGrid1.ShowPopupEditor(DBGrid1.Columns[7]);

Accès aux données d’un champ de référence


Pour accéder aux données d’un champ de référence, vous devez d’abord créer
un composant TDataSetField persistant puis pointer sur ce champ à l’aide de la
propriété DatasetField sur un composant TNestedTable ou TClientDataSet. Si la
référence est attribuée, la référence contient un enregistrement unique, avec les
données référencées. Si la référence vaut null, la référence est vide.
L’exemple suivant affecte des données à partir du champ de référence
CustomerRefCity à une boîte de saisie appelée CityEdit :
CityEdit.Text := CustomerRefCity.Fields[1].AsString;
CityEdit.Text := CustomerRefCity.NestedDataSet.Fields[1].AsString;
Lorsque les données d’un champ de référence sont modifiées, ce sont en réalité
les données référencées qui sont modifiées.
Avant d’affecter un champ de référence, vous devez utiliser une instruction
SELECT pour sélectionner la référence dans la table. Par exemple :
var
AddressQuery: TQuery;
CustomerAddressRef: TReferenceField;
begin
AddressQuery.SQL.Text := ‘SELECT REF(A) FROM AddressTable A WHERE A.City = ‘’San
Francisco’’’;
AddressQuery.Open;
CustomerAddressRef.Assign(AddressQuery.Fields[0]);
end;

19-32 Guide du développeur


Chapitre

20
Manipulation des tables
Chapter 20

Ce chapitre décrit l’utilisation du composant TTable dans vos applications de


bases de données. Le composant table encapsule la structure entière des données
dans une table de base de données sous-jacente. Il hérite la plupart de ses
méthodes et propriétés fondamentales de TDataSet, TBDEDataSet et TDBDataSet. C’est
pourquoi il est conseillé d’avoir lu au préalable les chapitres relatifs aux
ensembles de données (“Présentation des ensembles de données” et “Utilisation
des ensembles de données orientés BDE”).

Utilisation des composants table


Un composant table vous permet d’accéder à chacune des lignes
(enregistrements) et des colonnes (champs) d’une table sous-jacente, qu’elle
provienne de Paradox, dBASE, Access, FoxPro ou d’une base de données
compatible ODBC, ou encore d’une base de données SQL installée sur un
serveur distant comme InterBase, Sybase ou SQL Server.
Vous pouvez visualiser et éditer les données dans chaque colonne et ligne d’une
table. Vous pouvez aussi manipuler une portée de lignes et filtrer les
enregistrements pour extraire un sous-ensemble d’enregistrements à partir du
critère de filtre spécifié. Il est possible de rechercher des enregistrements, de
copier, de renommer ou de supprimer des tables entières et de créer des
relations maître/détail entre des tables.
Remarque Un composant table référence toujours une seule table de base de données. Si
vous devez accéder à plusieurs tables depuis un seul composant, ou si vous êtes
seulement intéressé par un sous-ensemble de lignes et de colonnes d’une ou de
plusieurs tables, il est préférable d’utiliser un composant requête. Pour plus
d’informations sur les composants requête, voir chapitre 21, “Manipulation des
requêtes”.

Manipulation des tables 20-1


Configuration d’un composant table

Configuration d’un composant table


Les étapes suivantes décrivent comment configurer un composant table lors de la
phase de conception. Selon l’application créée, vous devrez sans doute définir
des propriétés supplémentaires.
• Pour créer un composant table,
1 Depuis l’onglet AccèsBD de la palette des composants, placez un composant
table dans un module de données ou sur une fiche et donnez à sa propriété
Name une valeur appropriée pour votre application.
2 Affectez à la propriété DatabaseName du composant le nom de la base de
données à laquelle vous voulez accéder.
3 Donnez à la propriété TableName le nom de la table de la base de données.
Vous pouvez sélectionner les tables dans la liste déroulante si la propriété
DatabaseName a déjà été spécifiée.
4 Placez un composant source de données dans le module de données ou sur
la fiche, et affectez à sa propriété DataSet le nom du composant table. Le
composant source de données est utilisé pour transmettre aux composants
orientés données l’ensemble de résultats issu de la table.
• Pour accéder aux données encapsulées par un composant table,
5 Depuis l’onglet AccèsBD de la palette des composants, placez un composant
source de données dans le module de données ou sur la fiche, et affectez à
sa propriété DataSet le nom du composant table.
6 Placez un contrôle orienté données (comme TDBGrid) sur la fiche, puis
donnez à sa propriété DataSource le nom du composant source de données.
7 Mettez la propriété Active du composant table à True.

Spécification de l’emplacement d’une base de données


La propriété DatabaseName indique où le composant table doit chercher une table
de base de données. Pour les tables Paradox et dBASE, DatabaseName peut être
un alias BDE (Borland Database Engine) ou un chemin explicite. Pour les tables
SQL, vous devez obligatoirement spécifier un alias BDE.
L’avantage à spécifier un alias BDE réside dans la possibilité de changer de
source de données pour toute une application en modifiant simplement la
définition de l’alias en utilisant l’explorateur SQL. Pour modifier la définition de
l’alias en utilisant l’explorateur SQL, cliquez avec le bouton droit dans
l’explorateur et sélectionnez Renommer. Cela affiche les outils d’administration
BDE. Pour davantage d’informations sur la définition et l’utilisation d’alias BDE,
voir l’aide en ligne de l’explorateur SQL.
Pour définir la propriété DatabaseName,
1 Mettez, si nécessaire, la propriété Active de la table à False.
2 Spécifiez l’alias BDE ou le chemin d’accès dans la propriété DatabaseName.

20-2 Guide du développeur


Configuration d’un composant table

Astuce Si votre application utilise des composants base de données pour contrôler les
transactions, attribuez à DatabaseName un alias local défini pour le composant
base de données. Pour obtenir de plus amples informations sur les composants
base de données, reportez-vous au chapitre 17, “Connexion aux bases de
données”.

Spécification d’un nom de table


La propriété TableName indique à quelle table d’une base de données un
composant table est associé. Pour spécifier une table, procédez comme suit :
1 Si nécessaire, mettez la propriété Active de la table à False.
2 Affectez à la propriété DatabaseName un alias BDE ou un chemin d’accès. Pour
plus d’informations sur la définition de DatabaseName, voir “Spécification de
l’emplacement d’une base de données” à la page 20-2.
3 Donnez à la propriété TableName le nom de la table à laquelle vous voulez
accéder. Lors de la phase de conception, vous pouvez choisir un nom de table
correct dans la liste déroulante de la propriété TableName dans l’inspecteur
d’objets. A l’exécution, vous devez spécifier un nom correct dans le code.
Dès que la table porte un nom correct, vous pouvez mettre la propriété Active du
composant à True pour connecter la base de données, ouvrir la table et afficher
et éditer les données.
Lors de l’exécution, il est possible de définir ou changer la table associée à un
composant table. Pour cela, procédez comme suit :
• Mettez Active à False.
• Affectez un nom de table correct à la propriété TableName.
Ainsi, le code suivant change le nom de la table pour le composant table
OrderOrCustTable en fonction de son nom de table actuel :
with OrderOrCustTable do
begin
Active := False; {Close the table}
if TableName = 'CUSTOMER.DB' then
TableName := 'ORDERS.DB'
else
TableName := 'CUSTOMER.DB';
Active := True; {Reopen with a new table}
end;

Spécification du type des tables locales


Si une application accède à des tables Paradox, dBASE, FoxPro ou à des tables
texte ASCII délimitées par des virgules, le BDE peut utiliser la propriété
TableType pour déterminer le type de la table (sa structure). TableType n’est pas
utilisée lorsqu’une application accède à des tables SQL sur des serveurs de bases
de données.

Manipulation des tables 20-3


Configuration d’un composant table

Lorsque TableType a la valeur ttDefault, le BDE détermine le type de table à


partir de l’extension du nom de fichier. Le tableau 20.1 liste les extensions de
noms de fichiers reconnues par le BDE et les types de table reconnus :

Tableau 20.1 Types de tables reconnus par le BDE à partir des extensions de fichiers
Extension Type de table
Aucune extension Paradox
.DB Paradox
.DBF dBASE
.TXT texte ASCII

Si vos tables Paradox, dBASE et texte ASCII locales utilisent les extensions
décrites dans le tableau 20.1, vous pouvez laisser la propriété TableType à
ttDefault. Sinon, votre application devra définir TableType de façon à ce qu’elle
indique le type de table approprié. Le tableau 20.2 indique les valeurs pouvant
être affectées à TableType :

Tableau 20.2 Valeurs pouvant être affectées à la propriété TableType


Valeur Type de table
ttDefault Type de table déterminé automatiquement par le BDE.
ttParadox Paradox
ttDBase dBASE
ttFoxPro FoxPro
ttASCII Texte ASCII délimité par des virgules.

Ouverture et fermeture d’une table


Pour visualiser les données d’un contrôle orienté données, comme TDBGrid,
ouvrez une table. Deux moyens permettent d’ouvrir une table : vous pouvez
mettre sa propriété Active à True ou appeler sa méthode Open. L’ouverture
d’une table la place en mode dsBrowse et affiche ses données dans tout contrôle
actif associé à la source de données de la table.
Pour mettre fin à l’affichage et à l’édition des données ou pour changer les
valeurs des propriétés fondamentales d’un composant table (DatabaseName,
TableName et TableType), vous devez d’abord valider ou annuler les modifications
en suspens. Si les mises à jour en mémoire cache sont activées, appelez la
méthode ApplyUpdates pour écrire les modifications dans la base de données.
Vous pouvez ensuite fermer la table.
Deux moyens permettent de fermer une table : vous pouvez mettre sa propriété
Active à False ou appeler sa méthode Close. La fermeture d’une table la place en
mode dsInactive. Le contenu des contrôles actifs associés à la source de données
de la table est effacé.

20-4 Guide du développeur


Contrôle des privilèges d’écriture / lecture d’une table

Contrôle des privilèges d’écriture / lecture d’une table


Par défaut, un composant table demande des privilèges d’accès en lecture et en
écriture pour la table sous-jacente. Suivant les caractéristiques de celle-ci, le
privilège d’accès en écriture peut ne pas être accordé (par exemple, si vous avez
demandé un accès en écriture à une table SQL sur un serveur distant sur lequel
vous ne bénéficiez que d’un privilège d’accès en lecture).
Les composants table comportent trois propriétés pouvant affecter les privilèges
d’écriture et de lecture d’une table : CanModify, ReadOnly et Exclusive.
CanModify est une propriété en lecture seule qui spécifie si un composant table
peut accéder en lecture/écriture à la table de base de données sous-jacente.
Après avoir ouvert une table au moment de l’exécution, votre application
examine sa propriété CanModify pour déterminer si le serveur a accordé un accès
en écriture à la base de données. Si CanModify a la valeur False, l’application ne
peut pas écrire dans la base de données. Si CanModify est à True, l’application
peut écrire dans la base de données à condition que la propriété ReadOnly de la
table soit à False.
ReadOnly détermine si un utilisateur peut visualiser et éditer les données. Si
ReadOnly vaut False (la valeur par défaut), l’utilisateur peut visualiser et éditer
les données. Pour que l’utilisateur ne puisse que visualiser les données, mettez
ReadOnly à True avant d’ouvrir la table.
Exclusive contrôle si une application peut obtenir des accès en lecture/écriture sur
une table Paradox, dBASE ou FoxPro. Pour obtenir un droit exclusif d’accès en
lecture/écriture à une table de l’un de ces types, donnez à la propriété Exclusive
d’un composant table la valeur True avant de l’ouvrir. Si vous arrivez à ouvrir
une table en disposant d’un accès exclusif, les autres applications ne pourront ni
lire ni écrire de données dans cette table. La demande d’accès exclusif n’est pas
exhaussée si la table est déjà utilisée au moment où vous l’ouvrez.
Les instructions suivantes ouvrent une table en accès exclusif :
CustomersTable.Exclusive := True; {Définition de la demande de verrouillage exclusif}
CustomersTable.Active := True; {Maintenant, ouverture de la table}
Remarque Vous pouvez essayer Exclusive sur des tables SQL, mais certains serveurs ne
prennent pas en charge le verrouillage au niveau des tables. D’autres peuvent
accorder un verrouillage exclusif, mais permettront à certaines applications d’y
lire des données. Pour plus de détails sur le verrouillage exclusif des tables sur
votre serveur, reportez-vous à la documentation de celui-ci.

Recherche d’enregistrements
Différentes méthodes permettent de rechercher des enregistrements spécifiques
dans une table. Le moyen le plus souple consiste à utiliser les méthodes
génériques Locate et Lookup. Ces méthodes vous permettent de lancer une
recherche sur n’importe quel type de colonne dans n’importe quelle table, qu’elle
soit ou non indexée.

Manipulation des tables 20-5


Recherche d’enregistrements

• Locate place le curseur sur la première ligne correspondant au critère de


recherche spécifié.
• Lookup renvoie les valeurs de la première ligne répondant au critère de
recherche spécifié, mais ne place pas le curseur sur cette ligne.
Les méthodes Locate et Lookup peuvent être utilisées avec n’importe quel type
d’ensemble de données. Pour une présentation détaillée de ces méthodes, voir
chapitre 18, “Présentation des ensembles de données”.
Les composants table supportent les méthodes Goto et Find. Elles sont
documentées pour vous permettre de travailler avec les applications existantes,
mais il est fortement recommandé d’utiliser Lookup et Locate dans les nouvelles
applications. Toutefois, si vous implémentez les nouvelles méthodes dans vos
applications existantes après les avoir converties, vous constaterez une
augmentation des performances.

Recherche d’enregistrements à partir des champs indexés


Par mesure de compatibilité avec les applications créées sous les versions
précédentes, les composants table supportent les méthodes Goto. Elles vous
permettent de rechercher un enregistrement à partir de champs indexés, appelés
clés. Par ailleurs, le premier enregistrement trouvé avec ces méthodes devient
l’enregistrement en cours.
Pour les tables Paradox et dBASE, la clé doit toujours être un index (vous
pouvez le spécifier dans la propriété IndexName d’un composant table). Pour les
tables SQL, la clé peut être une liste de champs spécifiés dans la propriété
IndexFieldNames. Vous pouvez aussi spécifier une liste de champs pour les tables
Paradox ou dBASE, mais ces champs doivent être indexés. Pour plus
d’informations sur IndexName et IndexFieldNames, voir “Recherche avec un index
secondaire” à la page 20-9.
Astuce Pour effectuer une recherche sur des champs non indexés dans une table
Paradox ou dBASE, utilisez Locate. Une autre méthode consiste à utiliser un
composant TQuery et une instruction SELECT. Pour plus d’informations sur le
composant TQuery, voir chapitre 21, “Manipulation des requêtes”.
Le tableau suivant présente les six méthodes Goto et Find pouvant être utilisées
par une application pour rechercher un enregistrement :

Tableau 20.3 Méthodes de recherche du composant TTable


Méthode Utilisation
EditKey Préserve le contenu actuel du tampon de clés de recherche et place la table
en mode dsSetKey afin de permettre à votre application de rechercher des
valeurs avant l’exécution de la recherche.
FindKey Combine les méthodes SetKey et GotoKey en une seule méthode.
FindNearest Combine les méthodes SetKey et GotoNearest en une seule méthode.

20-6 Guide du développeur


Recherche d’enregistrements

Tableau 20.3 Méthodes de recherche du composant TTable (suite)


Méthode Utilisation
GotoKey Recherche le premier enregistrement d’un ensemble de données
correspondant exactement au critère de recherche et place le curseur dessus
s’il en trouve un.
GotoNearest Recherche dans des champs chaîne la correspondance la plus proche pour
un enregistrement, en se basant sur des valeurs de clé partielles et place le
curseur sur cet enregistrement.
SetKey Efface les éléments définis dans le tampon de clés de recherche et active
l’état dsSetKey pour la table afin de permettre à votre application de
spécifier un nouveau critère de recherche avant d’exécuter une recherche.

GotoKey et FindKey sont des fonctions booléennes qui, en cas de réussite,


déplacent le curseur sur un enregistrement correspondant et renvoient True. Si la
recherche n’aboutit pas, le curseur n’est pas déplacé et ces fonctions renvoient
False.
GotoNearest et FindNearest provoquent toujours le repositionnement du curseur
sur la première correspondance exacte trouvée ou, si aucune correspondance
n’est trouvée, sur le premier enregistrement supérieur au critère de recherche
spécifié.

Exécution d’une recherche avec les méthodes Goto


Pour exécuter une recherche en utilisant les méthodes Goto, procédez comme
suit :
1 Si nécessaire, spécifiez l’index voulu dans la propriété IndexName. S’il s’agit de
tables SQL, énumérez les champs à utiliser comme clé dans la propriété
IndexFieldNames. Si vous utilisez l’index primaire d’une table, il n’est pas
nécessaire de définir ces propriétés.
2 Ouvrez la table.
3 Mettez la table à l’état dsSetKey à l’aide de SetKey.
4 Spécifiez la ou les valeurs à rechercher dans Fields (c’est une liste de chaînes
que vous indexez à l’aide de nombres ordinaux correspondant à chaque
colonne ; le premier numéro de colonne d’une table est 0).
5 Recherchez et accédez au premier enregistrement trouvé avec GotoKey ou
GotoNearest.
Par exemple, le code ci-dessous, quand il est rattaché à l’événement OnClick d’un
bouton, passe au premier enregistrement contenant une valeur de champ
correspondant exactement au texte de la boîte de saisie d’une fiche :
procedure TSearchDemo.SearchExactClick(Sender: TObject);
begin
Table1.SetKey;
Table1.Fields[0].AsString := Edit1.Text;
if not Table1.GotoKey then
ShowMessage('Record not found');
end;

Manipulation des tables 20-7


Recherche d’enregistrements

GotoNearest est assez semblable. Elle recherche la première occurrence


correspondant à une valeur de champ partielle. Il est possible de l’utiliser
seulement pour les colonnes contenant des données de type chaîne. Exemple :
Table1.SetKey;
Table1.Fields[0].AsString := 'Sm';
Table1.GotoNearest;
S’il existe un enregistrement commençant par les lettres “Sm”, le curseur se
positionne dessus. Sinon, la position du curseur ne change pas et GotoNearest
renvoie False.

Exécution d’une recherche avec les méthodes Find


Pour exécuter une recherche en utilisant les méthodes Find, procédez comme
suit :
1 Si nécessaire, spécifiez l’index voulu dans la propriété IndexName. S’il s’agit de
tables SQL, énumérez les champs à utiliser comme clé dans la propriété
IndexFieldNames. Si vous utilisez l’index primaire d’une table, il n’est pas
nécessaire de définir ces propriétés.
2 Ouvrez la table.
3 Recherchez le premier enregistrement ou l’enregistrement le plus proche à
l’aide FindKey ou FindNearest. Les deux méthodes ne prennent qu’un seul
argument : une liste de valeurs délimitées par des virgules (chaque valeur
correspond à une colonne d’index de la table sous-jacente).
Remarque FindNearest ne peut être utilisée que pour les champs chaîne

Spécification de l’enregistrement en cours après une recherche


Par défaut, une recherche réussie provoque le positionnement du curseur sur le
premier enregistrement correspondant au critère de recherche. Si vous préférez,
vous pouvez mettre la propriété KeyExclusive d’un composant table à True afin
de positionner le curseur sur l’enregistrement suivant le premier enregistrement
correspondant au critère de recherche.
Par défaut, la propriété KeyExclusive est à False et positionne le curseur sur le
premier enregistrement correspondant au critère de recherche si celle-ci aboutit.

Recherche sur des clés partielles


Si une table comporte plusieurs colonnes clé et si vous voulez rechercher des
valeurs dans un sous-ensemble d’une clé, vous devez donner à KeyFieldCount
une valeur correspondant au nombre de colonnes sur lesquelles la recherche est
effectuée. Par exemple, si une table a une clé primaire sur trois colonnes et si
vous voulez effectuer une recherche sur seulement la première colonne, vous
devez donner à KeyFieldCount la valeur 1.

20-8 Guide du développeur


Recherche d’enregistrements

En ce qui concerne les tables avec des clés multicolonnes, vous ne pouvez
rechercher les valeurs que dans des colonnes contiguës, en commençant par la
première. Par exemple, pour une clé portant sur trois colonnes, vous pouvez
rechercher des valeurs dans la première colonne, puis dans la première et la
seconde, ou bien dans la première, la seconde et la troisième, mais pas seulement
dans la première et la troisième.

Recherche avec un index secondaire


Si vous voulez effectuer une recherche sur un index autre que l’index primaire
d’une table, vous devez spécifier son nom dans la propriété IndexName de la
table. Par exemple, si la table CUSTOMER a un index secondaire appelé
“CityIndex” et que vous voulez rechercher une valeur en utilisant cet index,
vous devez attribuer la valeur “CityIndex” à la propriété IndexName de la table
avant de commencer la recherche. Le code suivant peut vous servir d’exemple :
Table1.Close;
Table1.IndexName := 'CityIndex';
Table1.Open;
Table1.SetKey;
Table1['City'] := Edit1.Text;
Table1.GotoNearest;
Au lieu de spécifier un nom d’index, vous pouvez indiquer la liste des champs à
utiliser comme clé dans la propriété IndexFieldNames Pour les tables Paradox et
dBASE, les champs que vous listez doivent être indexés, sinon une exception
sera provoquée lors de la recherche. Avec les tables SQL, les champs de la liste
n’ont pas besoin d’être indexés.

Réitération ou extension d’une recherche


A chaque fois que vous faites appel à SetKey ou FindKey, les valeurs précédentes
de la propriété Fields sont effacées. Si vous voulez réitérer une recherche à l’aide
de champs préalablement définis, ou bien si vous voulez les ajouter aux champs
utilisés, faites appel à EditKey au lieu de SetKey et FindKey. Par exemple pour
étendre la recherche ci-dessus pour trouver un enregistrement avec un nom de
ville précis dans un pays donné, utilisez le code suivant :
Table1.EditKey;
Table1['Country'] := Edit2.Text;
Table1.GotoNearest;

Manipulation des tables 20-9


Tri d’enregistrements

Tri d’enregistrements
Un index définit l’ordre d’affichage des enregistrements d’une table. En général,
ils sont affichés par ordre croissant en fonction d’un index primaire (pour les
tables dBASE sans index primaire, l’ordre de tri dépend de l’ordre physique des
enregistrements). Ce comportement par défaut ne nécessite aucune intervention
de l’application. En revanche, si vous voulez obtenir un ordre de tri différent,
vous devez spécifier :
• Un index alternatif ou secondaire.
• Une liste de colonnes sur lesquelles doit s’opérer le tri (SQL seulement).
Pour spécifier un ordre de tri différent, procédez comme suit :
1 Déterminez les index disponibles.
2 Spécifiez l’index secondaire ou la liste de colonnes à utiliser.

Extraction d’une liste d’index disponibles avec GetIndexNames


Lors de son exécution, votre application peut faire appel à la méthode
GetIndexNames pour extraire la liste des index disponibles pour une table donnée.
GetIndexNames renvoie une liste de chaînes contenant des noms d’index valides.
Par exemple, le code ci-dessous détermine la liste des index disponibles pour
l’ensemble de données CustomersTable :
var
IndexList: TList;
ƒ
CustomersTable.GetIndexNames(IndexList);
Remarque Pour les tables Paradox, l’index primaire ne porte pas de nom ; il n’est donc pas
renvoyé par GetIndexNames. Si vous devez revenir à l’utilisation d’un index
primaire sur une table Paradox après avoir utilisé un index secondaire, donnez à
la propriété IndexName de cette table une valeur chaîne NULL.

Spécification d’un index secondaire avec IndexName


Pour spécifier qu’une table doit être triée avec un index secondaire, spécifiez le
nom de l’index dans la propriété IndexName du composant table. Lors de la
phase de conception, vous pouvez spécifier ce nom dans l’inspecteur d’objets. A
l’exécution, la propriété est accessible par programmation. Par exemple, le code
suivant affecte l’index CustDescending à la table CustomersTable :
CustomersTable.IndexName := 'CustDescending';

Spécification d’un fichier d’index dBASE


Pour les tables dBASE utilisant des index non inclus dans le fichier d’index
d’exploitation, vous devez donner à la propriété IndexFiles le nom du ou des
fichiers d’index utilisés. Au moment de la conception, vous pouvez cliquer sur le
bouton à points de suspension de la propriété IndexFiles dans l’inspecteur
d’objets pour lancer l’éditeur Fichiers index.

20-10 Guide du développeur


Tri d’enregistrements

Pour voir la liste des fichiers d’index disponibles, choisissez Ajouter et


sélectionnez un ou plusieurs fichiers d’index. Un fichier d’index dBASE peut
contenir plusieurs index. Pour en sélectionner un dans le fichier, choisissez-le
dans la liste déroulante IndexName de l’inspecteur d’objets. Vous pouvez
également spécifier plusieurs index dans le fichier en saisissant leur nom et en
les séparant par des points-virgules.
Vous pouvez également définir IndexFiles et IndexName au moment de
l’exécution. Par exemple, le code ci-dessous donne la valeur ANIMALS.MDX à
IndexFiles dans le composant table AnimalsTable, puis la valeur NAME à
IndexName :
AnimalsTable.IndexFiles := 'ANIMALS.MDX';
AnimalsTable.IndexName := 'NAME';

Spécification d’un ordre de tri pour les tables SQL


Dans SQL, l’ordre de tri des lignes est déterminé par la clause ORDER BY. Vous
pouvez spécifier l’index utilisé par cette clause en utilisant l’une des possibilités
suivantes :
• Au moyen de la propriété IndexName pour indiquer un index existant.
• Au moyen de la propriété IndexFieldNames pour créer un pseudo-index à
partir d’un sous-ensemble de colonnes de la table.
IndexName et IndexFieldNames sont des propriétés mutuellement exclusives. Si
vous définissez des valeurs pour l’une de ces propriétés, celles de l’autre sont
supprimées. Pour utiliser IndexName, reportez-vous à “Recherche avec un index
secondaire” à la page 20-9.

Spécification de champs avec IndexFieldNames


IndexFieldNames est une propriété liste de chaînes. Pour spécifier un ordre de tri,
vous devez indiquer les noms de colonnes dans l’ordre où ils doivent être
utilisés et les délimiter avec des points-virgules. Le tri ne se fait que par ordre
croissant. La distinction majuscules / minuscules n’est effectuée qu’en fonction
des possibilités du serveur. Reportez-vous à la documentation de votre serveur
pour en savoir plus sur ce point.
Le code ci-dessous définit l’ordre de tri de PhoneTable en fonction de LastName,
puis FirstName :
PhoneTable.IndexFieldNames := 'LastName;FirstName';
Remarque Si vous utilisez IndexFieldNames avec des tables Paradox et dBASE, Delphi
essaiera de trouver un index faisant appel aux colonnes que vous spécifiez. En
cas d’échec, il provoquera une exception.

Manipulation des tables 20-11


Manipulation d’un sous-ensemble de données

Vérification de la liste de champs d’un index


Lorsque votre application utilise un index au moment de son exécution, elle peut
examiner :
• La propriété IndexFieldCount pour déterminer le nombre de colonnes dans
l’index.
• La propriété IndexFields pour examiner le nom de chaque colonne de l’index.
IndexFields est une liste chaîne contenant les noms de colonnes de l’index.
L’extrait de code ci-dessous montre une utilisation possible d’IndexFieldCount et
d’IndexFields pour parcourir une liste de noms de colonnes dans une application :
var
I: Integer;
ListOfIndexFields: array[0 to 20} of string;
begin
with CustomersTable do
begin
for I := 0 to IndexFieldCount - 1 do
ListOfIndexFields[I] := IndexFields[I];
end;
end;
Remarque IndexFieldCount n’est pas utilisable dans une table de base ouverte sur un index
d’expression.

Manipulation d’un sous-ensemble de données


Les tables d’exploitation ont parfois une taille considérable, aussi les applications
doivent-elles souvent limiter le nombre de lignes sur lesquelles elles travaillent.
Pour les composants table, deux méthodes s’offrent à vous pour limiter le
nombre d’enregistrements extraits par une application : les filtres et les portées.
Les filtres peuvent être utilisés avec toute sorte d’ensemble de données, y
compris les composants TTable, TQuery et TStoredProc. Pour de plus amples
informations sur l’utilisation des filtres et des ensembles de données, voir
chapitre 18, “Présentation des ensembles de données”.
Les portées ne s’appliquent qu’aux composants TTable. Malgré leurs similarités
les portées et les filtres ont des utilisations différentes. La section suivante
présente les différences entre les portées et les filtres et explique comment
utiliser les portées.

Présentation des différences entre les portées et les filtres


Les portées et les filtres ont pour effet de restreindre la quantité
d’enregistrements visibles dans une table, mais leur mode de fonctionnement
diffère. Une portée est un ensemble d’enregistrements indexés contigus qui
correspondent tous aux valeurs des limites définies. Prenons l’exemple d’une

20-12 Guide du développeur


Manipulation d’un sous-ensemble de données

base de données d’employés indexée sur le nom de famille, vous pouvez


appliquer une portée pour afficher tous les employés dont le nom de famille est
supérieur à “Jones” et inférieur à “Smith”. Du fait que les portées dépendent des
index, elles ne peuvent être appliquées qu’à des champs indexés de tables
Paradox et dBASE (en ce qui concerne les tables SQL, les portées peuvent être
appliquées à tout champ spécifié dans la propriété IndexFieldNames). Les portées
ne peuvent être définies qu’à partir d’index existants.
Un filtre est composé d’un ensemble d’enregistrements contigus et non contigus
qui partagent les valeurs spécifiées. Supposons que vous souhaitiez appliquer un
filtre sur une base de données d’employés vivant en Californie et ayant travaillé
depuis au moins cinq ans dans l’entreprise. Bien qu’ils utilisent les index lors de
leur application, les filtres ne dépendent pas d’eux. Les filtres sont appliqués
enregistrement par enregistrement au fur et à mesure qu’une application
parcourt un ensemble de données.
En principe, les filtres sont plus souples que les portées. Toutefois, les portées
peuvent être plus efficaces lorsque les ensembles de données sont très grands et
que les enregistrements susceptibles d’intéresser l’application se trouvent déjà
dans des groupes d’index contigus. Pour les très grands ensembles de données, il
est souvent plus efficace d’utiliser une requête pour sélectionner les données à
visualiser et à éditer. Pour plus d’informations sur l’utilisation des filtres, voir
chapitre 18, “Présentation des ensembles de données”. Pour plus d’informations
sur l’utilisation des requêtes, voir chapitre 21, “Manipulation des requêtes”.

Création et application d’une nouvelle portée


Le processus de création et d’application d’une portée se déroule en plusieurs
étapes :
1 Mettez l’ensemble de données à l’état dsSetKey et définissez la valeur de début
de la portée.
2 Définissez la valeur de fin de la portée.
3 Appliquez la portée à l’ensemble de données.

Définition des valeurs de début de portée


Appelez la procédure SetRangeStart pour placer l’ensemble de données à l’état
dsSetKey et commencez à créer une liste de valeurs de début pour la portée.
Après l’appel à SetRangeStart, les affectations suivantes de la propriété Fields sont
traitées comme des valeurs d’index à utiliser lorsque la portée est appliquée. Si
vous utilisez des tables Paradox ou dBASE, les champs spécifiés doivent être des
champs indexés.
Supposons, par exemple, que votre application utilise un composant table appelé
Customers, lié à la table CUSTOMER, et que vous ayez créé des composants
champ persistants pour chaque champ de l’ensemble de données Customer. La
table CUSTOMER est indexée sur la première colonne (CustNo). Dans une fiche

Manipulation des tables 20-13


Manipulation d’un sous-ensemble de données

de l’application, deux composants de saisie appelés StartVal et EndVal permettent


d’indiquer les valeurs de début et de fin d’une portée. Dans un tel cas, le code
ci-dessous peut être utilisé pour créer une portée et l’appliquer :
with Customers do
begin
SetRangeStart;
FieldByName('CustNo') := StartVal.Text;
SetRangeEnd;
if EndVal.Text <> '' then
FieldByName('CustNo') := EndVal.Text;
ApplyRange;
end
Ce code vérifie que le texte saisi dans EndVal n’est pas NULL avant d’affecter
des valeurs à Fields. S’il est NULL, tous les enregistrements à partir du début de
la table seront inclus, puisque toutes les valeurs sont supérieures à une valeur
NULL. Par contre, si le texte entré dans EndVal a une valeur NULL, aucun
enregistrement ne sera inclus, puisqu’aucun ne peut être inférieur à cette valeur.
Pour un index à plusieurs colonnes, vous pouvez spécifier une valeur de départ
pour tous les champs de l’index ou pour certains de ces champs. Si aucune
valeur n’est fournie pour l’un des champs utilisé dans l’index, une valeur NULL
est affectée au champ lors de l’application de la portée. Si le nombre de valeurs
défini est supérieur au nombre de champs dans l’index, les champs
supplémentaires sont ignorés lors du calcul de la portée.
Pour mettre fin à la spécification du début de la portée, appelez SetRangeEnd ou
ApplyRange. Ces méthodes sont présentées dans les sections suivantes.
Astuce Pour commencer au début de l’ensemble de données, n’appelez pas
SetRangeStart.
Il est aussi possible de définir les valeurs de début (et de fin) d’une portée en
appelant la procédure SetRange. Pour plus d’informations sur SetRange, voir
“Définition des valeurs de début et de fin de portée” à la page 20-15.

Définition des valeurs de fin de portée


Appelez la procédure SetRangeEnd pour placer l’ensemble de données à l’état
dsSetKey et commencez à créer une liste de valeurs de fin pour la portée. Après
l’appel à SetRangeEnd, les affectations suivantes de la propriété Fields sont traitées
comme des valeurs d’index à utiliser lorsque la portée est appliquée. Si vous
utilisez des tables Paradox et dBASE, les champs spécifiés doivent être des
champs indexés.
Remarque Pour qu’une portée se termine sur le dernier enregistrement de l’ensemble de
données, spécifiez des valeurs de fin. Delphi suppose sinon que la valeur de fin
est une valeur NULL. Une portée contenant des valeurs de fin NULL est
toujours vide.

20-14 Guide du développeur


Manipulation d’un sous-ensemble de données

La façon la plus simple d’affecter des valeurs de fin de portée est d’appeler la
méthode FieldByName. Par exemple :
with Table1 do
begin
SetRangeStart;
FieldByName('LastName') := Edit1.Text;
SetRangeEnd;
FieldByName('LastName') := Edit2.Text;
ApplyRange;
end;
Pour un index à plusieurs colonnes, vous pouvez spécifier une valeur de départ
pour tous les champs de l’index ou pour certains de ces champs. Si aucune
valeur n’est fournie pour l’un des champs utilisés dans l’index, une valeur NULL
est affectée au champ lors de l’application de la portée. Si le nombre de valeurs
défini est supérieur au nombre de champs dans l’index, une exception est
provoquée.
Pour mettre fin à la spécification de fin de la portée, appelez ApplyRange. Pour
plus d’informations sur l’application d’une portée, voir “Application d’une
portée” à la page 20-16.
Remarque Il est aussi possible de définir les valeurs de fin (et de début) d’une portée en
appelant la procédure SetRange. Pour plus d’informations sur SetRange, voir la
section suivante.

Définition des valeurs de début et de fin de portée


Plutôt que d’utiliser des appels séparés à SetRangeStart et SetRangeEnd pour
spécifier les limites de la portée, vous pouvez appeler la procédure SetRange
pour placer l’ensemble de données à l’état dsSetKey et définir des valeurs de
début et de fin pour la portée par un simple appel.
SetRange prend deux paramètres tableau constants : un ensemble de valeurs de
début et un ensemble de valeurs de fin. Par exemple, les instructions suivantes
définissent une portée basée sur un index de deux colonnes :
SetRange([Edit1.Text, Edit2.Text], [Edit3.Text, Edit4.Text]);
Pour un index à plusieurs colonnes, vous pouvez spécifier une valeur de départ
pour tous les champs de l’index ou pour certains de ces champs. Si aucune
valeur n’est fournie pour l’un des champs utilisé dans l’index, une valeur NULL
est affectée au champ lors de l’application de la portée. Pour ne pas spécifier de
valeur pour le premier champ de l’index et spécifier des valeurs pour les champs
suivants, passez une valeur nil ou une valeur vide au premier champ. Si le
nombre de valeurs défini est supérieur au nombre de champs dans l’index, les
champs supplémentaires sont ignorés lors du calcul de la portée.
Remarque Pour qu’une portée se termine sur le dernier enregistrement de l’ensemble de
données, spécifiez des valeurs de fin. Delphi suppose sinon que la valeur de fin
est une valeur NULL. Une portée contenant des valeurs de fin NULL est
toujours vide car la portée de départ est supérieure ou égale à la portée de fin.

Manipulation des tables 20-15


Manipulation d’un sous-ensemble de données

Spécification d’une portée à partir de clés partielles


Si une clé est composée d’un ou de plusieurs champs chaîne, les méthodes
SetRange supportent les clés partielles. Par exemple, si un index est basé sur les
colonnes LastName et FirstName, les spécifications de portée suivantes sont
valides :
Table1.SetRangeStart;
Table1['LastName'] := 'Smith';
Table1.SetRangeEnd;
Table1['LastName'] := 'Zzzzzz';
Table1.ApplyRange;
Ce code inclut tous les enregistrements dans une portée où LastName est
supérieur ou égal à “Smith”. La spécification des valeurs peut également se
présenter ainsi :
Table1['LastName'] := 'Sm';
Cette instruction inclut les enregistrements où LastName est supérieur ou égal à
“Sm”. L’instruction suivante inclut les enregistrements où LastName est supérieur
ou égal à “Smith” et FirstName supérieur ou égal à “J” :
Table1['LastName'] := 'Smith';
Table1['FirstName'] := 'J';

Inclusion ou exclusion d’enregistrements correspondant aux valeurs d’une


portée
Par défaut, une portée inclut tous les enregistrements supérieurs ou égaux à la
portée de début spécifiée et inférieurs ou égaux à la portée de fin spécifiée. Ce
comportement est contrôlé par la propriété KeyExclusive. Par défaut, KeyExclusive
est à False.
Si vous préférez, vous pouvez donner à la propriété KeyExclusive d’un
composant table la valeur True pour qu’elle exclue les enregistrements égaux aux
portées de début et de fin spécifiées. Par exemple,
KeyExclusive := True;
Table1.SetRangeStart;
Table1['LastName'] := 'Smith';
Table1.SetRangeEnd;
Table1['LastName'] := 'Tyler';
Table1.ApplyRange;
Ce code inclut tous les enregistrements d’une portée pour lesquels LastName est
supérieur ou égal à “Smith” et inférieur à “Tyler”.

Application d’une portée


Les méthodes SetRange décrites dans les sections précédentes définissent les
limites d’une portée mais ne provoquent pas sa mise en effet. Pour qu’une
portée prenne effet, appelez la procédure ApplyRange. L’utilisateur ne peut plus
visualiser et accéder qu’aux données contenues dans le sous-ensemble de
l’ensemble de données.

20-16 Guide du développeur


Manipulation d’un sous-ensemble de données

Annulation d’une portée


La méthode CancelRange met fin à l’application d’une portée et restaure l’accès à
la totalité de l’ensemble de données. Même si l’annulation d’une portée restaure
l’accès à tous les enregistrements de l’ensemble de données, les conditions
relatives aux limites de la portée sont toujours disponibles afin que vous puissiez
réappliquer la portée ultérieurement. Les limites d’une portée sont préservées
jusqu’à ce que vous fournissiez de nouvelles limites ou modifiiez les limites
existantes.
Exemple :
ƒ
Table1.CancelRange;
ƒ
{permet d’utiliser cette portée ultérieurement. Pas besoin d’appeler SetRangeStart, etc.}
Table1.ApplyRange;
ƒ

Modification d’une portée


Deux fonctions permettent de modifier les conditions relatives aux limites d’une
portée : EditRangeStart (modification des valeurs de début d’une portée) et
EditRangeEnd (modification des valeurs de fin de la portée).
Le processus d’édition et d’application d’une portée se déroule comme suit :
1 Mettez l’ensemble de données à l’état dsSetKey et modifiez la valeur de début
de l’index pour la portée.
2 Modifiez la valeur de fin de l’index pour la portée.
3 Appliquez la portée à l’ensemble de données.
Vous pouvez modifier les valeurs de début ou de fin d’une portée, ou bien
modifier les deux à la fois. Si vous modifiez les conditions relatives aux limites
d’une portée actuellement appliquée à l’ensemble de données, les modifications
ne sont pas appliquées tant que vous n’avez pas appelé ApplyRange.

Modification du début de la portée


Appelez la procédure EditRangeStart pour placer l’ensemble de données à l’état
dsSetKey et commencez à modifier la liste en cours de valeurs de début de la
portée. Après avoir appelé EditRangeStart, les affectations suivantes de la
propriété Fields écrasent les valeurs d’index en cours lors de l’application de la
portée. Si vous utilisez des tables Paradox ou dBASE, les champs spécifiés
doivent être des champs indexés.
Astuce Si vous avez initialement créé une portée de début basée sur une clé partielle,
vous pouvez utiliser EditRangeStart pour étendre la valeur de début de la portée.
Pour plus d’informations sur les portées basées sur des clés partielles, voir
“Spécification d’une portée à partir de clés partielles” à la page 20-16.

Manipulation des tables 20-17


Suppression de tous les enregistrements d’une table

Modification de la fin de la portée


Appelez la procédure EditRangeEnd pour placer l’ensemble de données à l’état
dsSetKey et commencez à modifier la liste en cours de valeurs de fin de portée.
Après avoir appelé EditRangeEnd, les affectations suivantes de la propriété Fields
sont traitées comme des valeurs d’index de fin à utiliser lors de l’application
d’une portée. Si vous utilisez des tables Paradox ou dBASE, les champs doivent
être des champs indexés.
Remarque Spécifiez toujours des valeurs de fin pour une portée, même si vous voulez que
la portée se termine sur le dernier enregistrement de l’ensemble de données.
Delphi suppose sinon que la valeur de fin est une valeur NULL. Une portée
contenant des valeurs de fin NULL est toujours vide.

Suppression de tous les enregistrements d’une table


Pour supprimer toutes les lignes d’une table, appelez la méthodeEmptyTable du
composant table à l’exécution. Pour les tables SQL, cette méthode ne réussit que
si vous disposez du privilège DELETE sur la table. Par exemple, l’instruction
suivante provoque la destruction de tous les enregistrements d’un ensemble de
données :
PhoneTable.EmptyTable;
Attention Les données supprimées avec EmptyTable ne sont pas récupérables.

Suppression d’une table


A la conception, pour supprimer une table d’une base de données, cliquez avec
le bouton droit sur le composant table et sélectionnez Supprimer une table dans
le menu contextuel. Cette option de menu est présente uniquement si le
composant table représente réellement une table de base de données (les
propriétés DatabaseName et TableName désignent une table existante).
Pour supprimer une table à l’exécution, appelez la méthodeDeleteTable. Par
exemple, l’instruction ci-dessous supprime la table sous-jacente d’un ensemble de
données :
CustomersTable.DeleteTable;
Attention La suppression d’une table et de ses données avec DeleteTable est définitive.

Changement du nom d’une table


Pour renommer une table Paradox ou dBASE à la conception, cliquez avec le
bouton droit sur le composant table et sélectionnez Renommer une table dans le
menu contextuel. Vous pouvez également renommer une table existante en
remplaçant le nom de table spécifié par la propriété TableName dans l’inspecteur.
Lorsque vous modifiez la valeur de la propriété TableName, une boîte de

20-18 Guide du développeur


Création d’une table

dialogue demande si vous voulez renommer la table. Vous pouvez alors choisir
de renommer la table ou d’annuler l’opération ce qui modifie la propriété
TableName (par exemple pour créer une nouvelle table) sans modifier le nom de
la table représentée par l’ancienne valeur de TableName.
Pour renommer une table Paradox ou dBASE à l’exécution, appelez la méthode
RenameTable du composant table. Ainsi, l’instruction suivante change le nom de
la table Customer en CustInfo :
Customer.RenameTable(‘CustInfo’);

Création d’une table


Il est possible de créer de nouvelles tables de base de données à la conception et
à l’exécution. La commande Créer table (à la conception) ou la méthode
CreateTable (à l’exécution) permet de créer une table sans passer par du SQL. Elle
suppose néanmoins une connaissance intime des propriétés, événements et
méthodes des composants ensemble de données, en particulier TTable. Pour
définir la table à créer, procédez de la manière suivante :
• Affectez à la propriété DatabaseName la base de données qui va contenir la
nouvelle table.
• Affectez à la propriété TableType le type de la table. Pour les tables Paradox,
dBASE, ou Ascii, affectez respectivement à la propriété TableType, les valeurs
ttParadox, ttDBase ou ttASCII. Pour tous les autres types de table, affectez-lui
la valeur ttDefault.
• Affectez à la propriété TableName le nom de la nouvelle table. Si la propriété
TableType a la valeur ttParadox ou ttDBase, il n’est pas nécessaire de spécifier
l’extension.
• Ajoutez les définitions de champ décrivant les champs de la nouvelle table. A
la conception, vous pouvez ajouter les définitions de champ en double-
cliquant sur la propriété FieldDefs de l’inspecteur d’objets afin d’afficher
l’éditeur de collection. Utilisez l’éditeur de collection pour ajouter, retirer ou
modifier les propriétés des définitions de champ. A l’exécution, effacez les
définitions de champ existantes puis utilisez la méthode AddFieldDef pour
ajouter les nouvelles définitions de champ. Pour chaque nouvelle définition de
champ, affectez les propriétés de l’objet TFieldDef afin de spécifier les attributs
du champ.
• Il est également possible d’ajouter des définitions d’index décrivant les index
souhaités pour la nouvelle table. A la conception, vous pouvez ajouter de
nouvelles définitions d’index en double-cliquant sur la propriété IndexDefs
dans l’inspecteur d’objets afin d’afficher l’éditeur de collection. Utilisez
l’éditeur de collection pour ajouter, retirer ou modifier les propriétés des
définitions d’index. A l’exécution, effacez les définitions d’index existantes
puis utilisez la méthode AddIndexDef pour ajouter de nouvelles définitions
d’index. Pour chaque nouvelle définition d’index, affectez les propriétés de
l’objet TIndexDef afin de spécifier les attributs de l’index.

Manipulation des tables 20-19


Création d’une table

Remarque A la conception, il est possible de précharger les définitions de champ et d’index


d’une table existante dans les propriétés FieldDefs et IndexDefs. Affectez les
propriétés DatabaseName et TableName afin de spécifier la table existante. Cliquez
avec le bouton droit sur le composant table et choisissez l’option de mise à jour
de la définition de table. Cela initialise automatiquement les valeurs des
propriétés FieldDefs et IndexDefs afin de décrire les champs et index de la table
existante. Puis, réinitialisez les propriétés DatabaseName et TableName afin de
spécifier la table à créer (en refusant de renommer la table existante). Si vous
voulez stocker ces définitions avec le composant table (si, par exemple,
l’application va les employer pour créer des tables sur le système de
l’utilisateur), affectez la valeur True à la propriété StoreDefs.
Une fois la table entièrement décrite, il est enfin possible de la créer. A la
conception, cliquez avec le bouton droit sur le composant table et choisissez
Créer une table. A l’exécution appelez la méthode CreateTable pour générer la
table spécifiée.
Attention Si vous créez une table dupliquant le nom d’une table existante, la table
existante et toutes ses données sont remplacées par la nouvelle table. Il est
impossible de récupérer l’ancienne table et ses données.
Le code suivant crée une nouvelle table à l’exécution et l’associe à l’alias
DBDEMOS. Avant de créer la nouvelle table, il vérifie que le nom de table
spécifié ne correspond pas à celui d’une table existante :
var
NewTable: TTable;
NewIndexOptions: TIndexOptions;
TableFound: Boolean;
begin
NewTable := TTable.Create;
NewIndexOptions := [ixPrimary, ixUnique];
with NewTable do
begin
Active := False;
DatabaseName := 'DBDEMOS';
TableName := Edit1.Text;
TableType := ttDefault;
FieldDefs.Clear;
FieldDefs.Add(Edit2.Text, ftInteger, 0, False);
FieldDefs.Add(Edit3.Text, ftInteger, 0, False);
IndexDefs.Clear;
IndexDefs.Add('PrimaryIndex’, Edit2.Text, NewIndexOptions);
end;
{Now check for prior existence of this table}
TableFound := FindTable(Edit1.Text); {code for FindTable not shown}
if TableFound = True then
if MessageDlg('Overwrite existing table ' + Edit1.Text + '?', mtConfirmation,
mbYesNo, 0) = mrYes then
TableFound := False;
if not TableFound then
CreateTable; { create the table}
end;
end;

20-20 Guide du développeur


Importation des données d’une autre table

Importation des données d’une autre table


Vous pouvez utiliser la méthode BatchMove du composant table pour importer
des données d’une autre table. BatchMove permet de :
• Copier des enregistrements d’une autre table dans cette table.
• Mettre à jour des enregistrements de la table à partir d’enregistrements d’une
autre table.
• Ajouter des enregistrements d’une autre table à la fin de cette table.
• Supprimer dans cette table des enregistrements communs à une autre table.
BatchMove accepte deux paramètres : le nom de la table contenant les données à
importer et une spécification de mode qui détermine l’opération d’importation à
effectuer. Le tableau 20.4 décrit les valeurs possibles pour spécifier le mode
d’importation :

Tableau 20.4 Modes d’importation de BatchMove


Valeur Signification
batAppend Ajoute tous les enregistrements de la table source à la fin de cette table.
batAppendUpdate Ajoute tous les enregistrements de la table source à la fin de cette table
et met à jour les enregistrements existant aussi dans la table source.
batCopy Copie tous les enregistrements de la table source dans cette table.
batDelete Supprime de cette table tous les enregistrements existant aussi dans la
table source.
batUpdate Met à jour les enregistrements existant aussi dans la table source.

Par exemple, le code suivant met à jour les enregistrements de la table en cours
à partir des enregistrements de la table Customer :
Table1.BatchMove('CUSTOMER.DB', batUpdate);
BatchMove renvoie le nombre d’enregistrements importés.
Attention L’importation d’enregistrements en utilisant le mode batCopy écrase les
enregistrements existants. Pour préserver ces enregistrements, vous devez plutôt
utiliser batAppend.
Seules quelques fonctions disponibles pour votre application sont directement
effectuées par BatMove par l’intermédiaire du composant TBatchMove. Si vous
devez déplacer une grande quantité de données entre des tables, utilisez le
composant action groupée (TBatchMove) au lieu d’appeler la fonction BatchMove
de la table. Pour plus d’informations sur l’utilisation de TBatchMove, voir la
rubrique suivante, “Utilisation de TBatchMove.”

Manipulation des tables 20-21


Utilisation de TBatchMove

Utilisation de TBatchMove
TBatchMove encapsule des fonctionnalités du moteur de bases de données
Borland (BDE) qui vous permettent de dupliquer un ensemble de données,
d’ajouter des enregistrements d’un ensemble de données à un autre, de mettre à
jour les enregistrements d’un ensemble de données à partir de ceux d’un autre et
de supprimer dans un ensemble de données les enregistrements existant dans un
autre ensemble de données. TBatchMove est souvent utilisé pour :
• Charger les données d’un serveur dans une source de données locale afin de
les analyser ou d’effectuer d’autres opérations.
• Transférer, dans le cadre d’une opération d’upsizing, une base de données
dans des tables stockées sur un serveur distant.
Le composant action groupée peut créer sur la destination des tables
correspondant aux tables source, en établissant une correspondance automatique
entre les noms de colonne et les types de données.

Création d’un composant action groupée


Pour créer un composant action groupée :
1 Placez sur une fiche ou dans un module de données le composant table ou
requête contenant les enregistrements que vous voulez importer (appelé
ensemble de données Source).
2 Placez sur une fiche ou dans un module de données le composant ensemble
de données dans lequel vous voulez importer les enregistrements (appelé
ensemble de données Destination).
3 Depuis l’onglet AccèsBD de la palette des composants, placez un composant
TBatchMove dans un module de données ou sur une fiche et donnez à sa
propriété Name une valeur unique appropriée à votre application.
4 Donnez à la propriété Source du composant action groupée le nom de la table
devant servir à la copie, à l’ajout ou à la mise à jour. Vous pouvez
sélectionner les tables dans une liste déroulante des composants ensemble de
données disponibles.
5 Donnez à la propriété Destination le nom de l’ensemble de données à créer, à
compléter ou à mettre à jour. Vous pouvez sélectionner une table de
destination dans une liste déroulante des composants ensemble de données
disponibles ou ajouter un nouveau composant table en guise de destination.
Si vous ajoutez, mettez à jour ou supprimez des informations, la table
spécifiée pour la propriété Destination doit être une table existante.
Si vous copiez une table et que la propriété Destination indique le nom d’une
table existante, l’exécution de l’action groupée écrase toutes les données
contenues dans la table de destination.

20-22 Guide du développeur


Utilisation de TBatchMove

Si vous créez une table entièrement nouvelle en copiant une table existante, la
table obtenue possède le nom spécifié dans la propriété Name du composant
table vers lequel vous copiez les informations. Le type de table obtenu
présente une structure appropriée au serveur spécifié par la propriété
DatabaseName.
6 Définissez la propriété Mode pour indiquer le type d’opération à réaliser. Les
opérations possibles sont batAppend (opération par défaut), batUpdate,
batAppendUpdate, batCopy et batDelete. Pour plus d’informations sur ces modes,
voir “Spécification d’un mode d’action groupée” à la page 20-23
7 Facultatif : définissez la propriété Transliterate. Lorsqu’elle est à true (valeur
par défaut), les données caractère sont transcrites du jeu de caractères de
l’ensemble de données Source vers le jeu de caractères de l’ensemble de
données Destination, en cas de différence.
8 Facultatif : définissez une association entre les colonnes à l’aide de la propriété
Mappings. Vous n’avez pas à utiliser cette propriété si vous voulez que l’action
groupée associe chaque colonne en fonction de sa position dans les tables source
et destination. Pour plus d’informations sur les correspondances de colonne, voir
“Etablissement d’une correspondance entre les types de données” à la page 20-25.
9 Facultatif : définissez les propriétés ChangedTableName, KeyViolTableName et
ProblemTableName. L’action groupée stocke les enregistrements posant
problème dans la table spécifiée par ProblemTableName. Si vous mettez à jour
une table Paradox par une opération action groupée, les violations de clés
seront signalées dans la table spécifiée par KeyViolTableName.
ChangedTableName liste tous les enregistrements modifiés dans la table
destination lors de l’action groupée. Si vous ne spécifiez pas ces propriétés, les
tables d’erreur ne sont ni créées, ni utilisées. Pour plus d’informations sur la
gestion des erreurs, voir “Gestion des erreurs relatives aux actions groupées”
à la page 20-26.

Spécification d’un mode d’action groupée


La propriété Mode spécifie l’action réalisée par un composant action groupée :

Tableau 20.5 Modes relatifs aux actions groupées


Propriété Fonction
batAppend Ajoute les enregistrements à la table destination.
batUpdate Met à jour les enregistrements de la table destination avec les
enregistrements correspondants de la table source. La mise à jour est
basée sur l’index en cours dans la table destination.
batAppendUpdate Si un enregistrement correspondant existe dans la table destination, une
mise à jour est effectuée. Sinon, les enregistrements sont ajoutés à la
table destination.
batCopy Crée la table destination à partir de la structure de la table source. Si la
table destination existe déjà, elle est supprimée et recréée.
batDelete Supprime les enregistrements de la table destination ayant une
correspondance dans la table source.

Manipulation des tables 20-23


Utilisation de TBatchMove

Ajout
Pour ajouter des données, l’ensemble de données destination doit déjà exister. Si
nécessaire le BDE convertit les données à des tailles et des types de données
appropriés à l’ensemble de données destination. Si la conversion n’est pas
possible, une exception est provoquée et les données ne sont pas ajoutées.

Mise à jour
Pour mettre à jour les données, l’ensemble de données destination doit déjà
exister et avoir un index qui permet d’établir la correspondance entre les
enregistrements. Si les champs de l’index primaire sont utilisés pour établir les
correspondances, les enregistrements pour lesquels des champs indexés dans
l’ensemble de données destination correspondent à des champs indexés dans
l’ensemble de données source sont écrasés avec les données source. Si nécessaire,
le BDE convertit les données à des tailles et des types de données appropriés à
l’ensemble de données destination.

Ajout et mise à jour


Pour ajouter et mettre à jour des données, l’ensemble de données destination
doit déjà exister et avoir un index qui permet d’établir la correspondance entre
les enregistrements. Si les champs de l’index primaire sont utilisés pour établir
les correspondances, les enregistrements pour lesquels des champs indexés dans
l’ensemble de données destination correspondent à des champs indexés dans
l’ensemble de données source sont écrasés avec les données source. Sinon, les
données de l’ensemble de données source sont ajoutées dans l’ensemble de
données destination. Si nécessaire, le BDE convertit les données à des tailles et
des types de données appropriés à l’ensemble de données destination.

Copie
Pour faire une copie de l’ensemble de données source, l’ensemble de données
destination ne doit pas exister. Si c’est le cas, l’opération action groupée
provoque l’écrasement de l’ensemble de données destination par une copie de
l’ensemble de données source.
Si les ensembles de données source et destination sont gérés par des moteurs de
bases de données de types différents, comme, par exemple Paradox et InterBase,
le BDE crée un ensemble de données destination avec une structure aussi proche
que possible de celle de l’ensemble de données source et effectue
automatiquement les conversions de taille et de type de données en cas de
besoin.
Remarque TBatchMove ne copie pas les structures de métadonnées comme les index, les
contraintes et les procédures stockées. Vous devez recréer ces objets métadonnées
sur votre serveur de bases de données ou utiliser l’explorateur SQL.

20-24 Guide du développeur


Utilisation de TBatchMove

Suppression
Pour supprimer des données, l’ensemble de données destination doit déjà exister
et avoir un index qui permet d’établir la correspondance entre les
enregistrements. Si les champs de l’index primaire sont utilisés pour établir les
correspondances, les enregistrements pour lesquels des champs indexés dans
l’ensemble de données destination correspondent à des champs indexés dans
l’ensemble de données source sont supprimés dans la table destination.

Etablissement d’une correspondance entre les types de données


En mode batAppend, un composant action groupée crée la table destination à
partir des types de données des colonnes de la table source. Les colonnes et les
types de données sont mis en correspondance à partir de leur position dans les
tables source et destination : la première colonne dans la source est associée à la
première colonne de la destination, la deuxième à la deuxième, et ainsi de suite.
Pour outrepasser la mise en correspondance par défaut des colonnes, utilisez la
propriété Mappings. Cette propriété est une liste d’affectations de colonnes (une par
ligne), qui peut prendre deux formes. Pour associer une colonne de la table source
à une colonne du même nom dans la table destination, vous pouvez utiliser une
liste simple contenant le nom de la colonne à associer. Par exemple, l’association
ci-dessous indique que la colonne appelée NomDeColonne dans la table source doit
être associée à une colonne de même nom dans la table destination :
NomDeColonne
Pour associer la colonne ColonneSource de la table source à la colonne
ColonneDestination de la table destination, utilisez l’instruction suivante :
ColonneDestination = ColonneSource
Si les types de données des colonnes source et destination sont différents,
l’opération action groupée tentera de les traduire au mieux. Elle tronquera si
nécessaire les types “Caractère”, et tentera, si possible, de faire pour vous un
début de conversion. Par exemple, si vous associez une colonne CHAR(10) à une
colonne CHAR(5), les 5 derniers caractères de la colonne source sont tronqués.
A titre d’exemple de conversion, si une colonne source de type Caractère est
associée à une colonne destination de type Entier, l’opération action groupée
convertit la valeur alphanumérique ‘5’ en son équivalent entier. Si une valeur ne
peut être convertie, une erreur s’affiche. Pour plus d’informations sur les erreurs,
voir “Gestion des erreurs relatives aux actions groupées” à la page 20-26.
Lorsque vous déplacez des données entre des tables de types différents, un
composant action groupée se charge de traduire les types de données conformément
aux types de serveurs de l’ensemble de données. Pour plus d’informations sur les
correspondances entre les types de serveurs, voir l’aide en ligne du BDE.
Remarque Pour exécuter une action groupée sur des données en direction d’une base de
données sur serveur SQL, vous devez avoir installé ce serveur de bases de
données et une version de Delphi proposant le pilote SQL Link approprié ou
utiliser ODBC si les pilotes ODBC des tierces parties sont installés.

Manipulation des tables 20-25


Utilisation de TBatchMove

Exécution d’une action groupée


A l’exécution, la méthode Execute permet d’exécuter une opération action
groupée préparée. Par exemple, si BatchMoveAdd est le nom du composant action
groupée, l’instruction ci-dessous l’exécute :
BatchMoveAdd.Execute;
Vous pouvez aussi exécuter une opération action groupée pendant la phase de
conception en cliquant avec le bouton droit de la souris sur un composant action
groupée et en choisissant Exécuter dans le menu contextuel.
La propriété MovedCount garde en mémoire le nombre d’enregistrements qui ont
été déplacés lors de l’exécution d’une action groupée.
La propriété RecordCount est utilisée pour contrôler le nombre d’enregistrements
maximum qui seront déplacés. Si elle vaut zéro, tous les enregistrements sont
déplacés, en commençant par le premier enregistrement de la table source. Si
RecordCount est un nombre positif, le nombre d’enregistrements maximum défini
dans RecordCount est déplacé, en commençant par l’enregistrement en cours dans
l’ensemble de données source. Si RecordCount dépasse le nombre
d’enregistrements restant dans la table source, l’opération se termine lorsque la
fin de l’ensemble de données source est atteinte. Vous pouvez examiner
MoveCount pour déterminer le nombre d’enregistrements effectivement transférés.

Gestion des erreurs relatives aux actions groupées


Deux types d’erreurs peuvent se produire dans une opération action groupée :
les erreurs de conversion de type de données et les violations d’intégrité. Le
composant TBatchMove possède certaines propriétés indiquant comment ces
erreurs doivent être traitées.
La propriété AbortOnProblem indique si l’opération doit être annulée en cas
d’erreur de conversion de type de données. Si AbortOnProblem est à True,
l’opération est annulée lorsqu’un problème survient. Si elle est à False, l’opération
continue. Vous pouvez examiner la table spécifiée dans la propriété
ProblemTableName pour savoir quels sont les enregistrements posant problème.
La propriété AbortOnKeyViol indique si l’opération doit être annulée en cas de
violation de clé Paradox.
La propriété ProblemCount indique le nombre d’enregistrements qui n’ont pu être
ajoutés dans la table destination, car leur ajout aurait entraîné une perte de
données. Si AbortOnProblem est à True, ce nombre vaut un, puisque l’opération
est annulée lorsque l’erreur se produit.
Les propriétés suivantes autorisent un composant action groupée à créer des
tables supplémentaires documentant l’opération effectuée :
• ChangedTableName, si spécifiée, crée une table Paradox locale contenant tous
les enregistrements de la table destination qui ont été modifiés suite à des
opérations impliquant une suppression ou des mises à jour.

20-26 Guide du développeur


Synchronisation de tables liées à la même table

• KeyViolTableName, si spécifiée, crée une table Paradox locale contenant tous les
enregistrements de la table source qui ont généré une violation d’intégrité lors
de l’utilisation d’une table Paradox. Si AbortOnKeyViol est à True, cette table
contient au plus un enregistrement, car l’opération sera annulée après ce
premier enregistrement.
• ProblemTableName, si spécifiée, crée une table Paradox locale contenant tous les
enregistrements qui n’ont pas pu être placés dans la table destination en
raison d’erreurs de conversion de type de données (si, par exemple, la table
ne pouvait pas contenir les enregistrements d’une table source dont les
données ont dû être tronquées pour tenir dans la table destination). Si
AbortOnProblem est à True, il y a au plus un enregistrement dans la table, car
l’opération est annulée après ce premier problème.
Remarque Si ProblemTableName n’est pas spécifiée, les données de l’enregistrement sont
tronquées et placées dans la table destination.

Synchronisation de tables liées à la même table


Si plusieurs composants table sont liés à la même table par leurs propriétés
DatabaseName et TableName et si les tables ne partagent pas de composant source
de données, chacune d’elles a sa propre vue des données et son propre
enregistrement en cours. Au fur et à mesure que les utilisateurs accèdent aux
enregistrements par l’intermédiaire des composants table, les enregistrements en
cours de ces derniers varient.
La méthode GotoCurrent permet de faire en sorte que l’enregistrement en cours
de chacun de ces composants table soit toujours le même. Cette méthode
synchronise l’enregistrement en cours d’une table avec celui d’un autre
composant table. Par exemple, le code ci-dessous définit l’enregistrement en
cours de CustomerTableOne comme étant identique à celui de CustomerTableTwo :
CustomerTableOne.GotoCurrent(CustomerTableTwo);
Astuce Si votre application doit synchroniser des composants table de cette manière,
placez-les dans un module de données et incluez l’en-tête du module de données
pour chaque unité accédant aux tables.
Pour synchroniser des composants table se trouvant sur différentes fiches, vous
devez inclure le fichier en-tête de l’une des fiches dans l’unité de l’autre fiche et
qualifier l’un des noms de table avec le nom de la fiche. Exemple :
CustomerTableOne.GotoCurrent(Form2.CustomerTableTwo);

Création de fiches maître-détail


Les propriétés MasterSource et MasterFields d’un composant table peuvent
permettre d’établir une relation un-à-plusieurs entre deux tables.

Manipulation des tables 20-27


Création de fiches maître-détail

La propriété MasterSource permet de spécifier une source de données utilisée par


la table détail pour extraire des données de la table maître. Par exemple, si vous
liez deux tables dans une relation maître-détail, la table détail pourra se charger
du suivi des événements se produisant dans la table maître en spécifiant le
composant source de données de la table maître dans cette propriété.
La propriété MasterFields spécifie les colonnes communes aux deux tables qui
permettent d’établir le lien. Pour lier des tables à partir de plusieurs noms de
colonnes, vous devez utiliser une liste délimitée par des points-virgules :
Table1.MasterFields := 'OrderNo;ItemNo';
Pour créer des liens fiables entre deux tables, vous pouvez utiliser le concepteur
de liaisons de champs. Pour plus d’informations sur le concepteur de liaisons de
champs, voir le Guide de l’utilisateur.

Construction d’une fiche maître-détail exemple


En suivant les procédures décrites ci-dessous, vous pourrez créer une fiche dans
laquelle un utilisateur fera défiler les enregistrements sur des clients et affichera
toutes les commandes passées par le client en cours. La table maître est
CustomersTable, la table détail OrdersTable.
1 Placez deux composants TTable et deux composants TDataSource dans un
module de données.
2 Définissez les propriétés du premier composant TTable comme suit :
• DatabaseName : DBDEMOS
• TableName : CUSTOMER
• Name : CustomersTable
3 Définissez les propriétés du second composant TTable comme suit :
• DatabaseName : DBDEMOS
• TableName : ORDERS
• Name : OrdersTable
4 Définissez les propriétés du premier composant TDataSource comme suit :
• Name : CustSource
• DataSet : CustomersTable
5 Définissez les propriétés du second composant TDataSource comme suit :
• Name : OrdersSource
• DataSet : OrdersTable
6 Placez deux composants TDBGrid sur une fiche.
7 Choisissez Fichier|Inclure l’en-tête d’unité pour indiquer que la fiche doit
utiliser le module de données.
8 Donnez à la propriété DataSource du premier composant grille la valeur
“DataModule2->CustSource” et à la propriété DataSource du second composant
grille la valeur “DataModule2->OrdersSource”.

20-28 Guide du développeur


Utilisation des tables imbriquées

9 Donnez à la propriété MasterSource de OrdersTable la valeur “CustSource”. Cette


étape lie la table CUSTOMER (table maître) à la table ORDERS (table détail).
10 Dans l’inspecteur d’objets, double-cliquez sur la boîte des valeurs de la
propriété MasterFields pour lancer le concepteur de liaison de champs afin de
définir les propriétés suivantes :
• Dans le champ Index disponibles, choisissez CustNo pour lier les deux
tables d’après le champ CustNo.
• Sélectionnez CustNo dans les listes Champs détail et Champs maître.
• Cliquez sur le bouton Ajouter pour ajouter cette condition de jointure. Dans
la liste Champs joints apparaît “CustNo -> CustNo”.
• Choisissez OK pour valider vos sélections et quitter le concepteur de liaison
de champs.
11 Donnez aux propriétés Active de CustomersTable et OrdersTable la valeur True
afin d’afficher les données dans les grilles de la fiche.
12 Compilez l’application et exécutez-la.
Si vous lancez l’application maintenant, vous pouvez constater que les tables
sont liées et que quand vous passez à un enregistrement différent de la table
CUSTOMER, seuls apparaissent les enregistrements de la table ORDERS
appartenant au client affiché.

Utilisation des tables imbriquées


Un composant table imbriquée permet d’accéder aux données d’un ensemble de
données imbriqué d’une table. La propriété NestedDataSet d’un champ ensemble
de données imbriqué persistant contient une référence à l’ensemble de données
imbriqué. Comme TNestedDataSet provient de TBDEDataSet, une table
imbriquée hérite de la fonctionnalité BDE et utilise le moteur de bases de
données Borland (BDE) pour accéder aux données de la table imbriquée. Une
table imbriquée offre presque les mêmes possibilités qu’un composant table mais
les données auxquelles elle accède sont stockées dans une table imbriquée.

Configuration d’un composant table imbriquée


La procédure suivante regroupe les instructions générales permettant de
configurer un composant table imbriquée lors de la conception. Un composant
table ou requête modifiable doit être disponible et être en mesure d’accéder à un
ensemble de données contenant un champ ensemble de données ou référence.
Un champ persistant pour TDataSetField ou TReferenceField doit également déjà
exister. Voir “Utilisation des champs ensemble de données” à la page 19-30.

Manipulation des tables 20-29


Utilisation des tables imbriquées

Pour utiliser un composant table imbriquée,


1 Placez un composant table imbriquée à partir de l’onglet AccèsBD de la palette
des composants dans un module de données ou sur une fiche et attribuez à sa
propriété Name une valeur unique appropriée à votre application.
2 Attribuez au paramètre DataSetField du composant le nom du champ ensemble
de données ou référence persistant auquel il faut accéder. Vous pouvez
sélectionner les champs à partir de la liste déroulante.
3 Placez un composant source de données dans le module de données ou sur la
fiche et attribuez à sa propriété DataSet le nom du composant table imbriquée.
Le composant source de données permet de transmettre un ensemble de
résultats de la table aux composants orientés données en vue de son affichage.

20-30 Guide du développeur


Chapitre

Manipulation des requêtes


Chapter 21
21
Ce chapitre décrit l’utilisation du composant ensemble de données TQuery ; il
vous permet d’utiliser des instructions SQL pour accéder à des données. Pour ce
faire, vous devez en principe avoir pris connaissance des généralités sur les
sources de données et les ensembles exposés dans le chapitre 18, “Présentation
des ensembles de données”.
Un composant requête encapsule une instruction SLQ utilisée dans une
application client pour récupérer, insérer, mettre à jour et supprimer des données
d’une ou de plusieurs tables de bases de données. SQL est un langage de bases
de données relationnelles standard utilisé par la plupart des fournisseurs de
SGBD sur serveur, tels que Sybase, Oracle, InterBase et Microsoft SQL Server.
Vous pouvez utiliser les composants requête avec des serveurs de bases de
données distants (seulement si votre version de Delphi contient SQL Links), avec
Paradox, dBASE, FoxPro et Access, ainsi qu’avec des bases de données
compatibles ODBC.

Pour une utilisation efficace des requêtes


Pour utiliser avec efficacité le composant requête, vous devez être familiarisé avec :
• SQL et l’implémentation SQL de votre serveur ; y compris les limitations et les
extensions du standard SQL-92.
• Le moteur de base de données Borland (BDE).
Si vous êtes un développeur de base de données de bureau expérimenté, et que
vous voulez concevoir des applications sur serveur, reportez-vous à
“Informations pour les développeurs d’applications de bureau” à la page 21-2
avant de lire le reste de ce chapitre. Si vous ne connaissez pas le langage SQL,
nous vous conseillons d’acheter l’un des nombreux manuels couvrant le sujet,
dont Understanding the New SQL: A Complete Guide, par Jim Melton et Alan R.
Simpson, Morgan Kaufmann Publishers.

Manipulation des requêtes 21-1


Pour une utilisation efficace des requêtes

Si vous êtes un développeur de serveurs de base de données expérimenté, vous


connaissez sans aucun doute les rudiments du langage SQL et ceux de votre
serveur. Toutefois, vous souhaitez probablement approfondir vos connaissances
sur les applications client Delphi et le BDE. Pour plus d’informations sur les
requêtes et le BDE, voir “Informations pour les développeurs d’applications sur
serveur” à la page 21-3.

Informations pour les développeurs d’applications de bureau


En tant que développeur d’applications de bureau, vous connaissez déjà le
paradigme de Delphi du BDE (ce paradigme repose sur les tables, les
enregistrements et les champs). Il est sans doute facile pour vous d’utiliser un
composant TTable pour accéder à chaque champ de données des enregistrements
d’un ensemble de données. Vous savez que lorsqu’on définit la propriété
TableName d’une source de données, on spécifie en même temps le nom de la
table de base de données à laquelle on veut accéder.
Il est également fort probable que vous ayez déjà défini une propriété de filtre et
des méthodes de portée pour un composant TTable afin de restreindre le nombre
d’enregistrements visibles à un moment donné dans une application.
L’affectation d’une portée limite temporairement l’accès aux données à un bloc
d’enregistrements indexés contigus répondant à certaines conditions (comme
n’afficher que les enregistrements correspondant aux employés dont le nom est
supérieur ou égal à “Jones” et inférieur ou égal à “Smith”). La définition d’un
filtre restreint temporairement l’accès aux données à un ensemble
d’enregistrements qui généralement ne se suivent pas et qui répondent à un
critère de filtre (comme n’afficher que les enregistrements correspondant à des
clients ayant une adresse en Californie).
Il existe de nombreuses similitudes dans le comportement des requêtes et des
filtres de tables. Toutefois, la principale différence réside dans le fait qu’avec une
requête, vous utilisez la propriété SQL du composant requête (et parfois sa
propriété Params) pour identifier les enregistrements à insérer, à supprimer ou à
mettre à jour dans un ensemble de données. Par certains côtés, une requête est
souvent plus puissante qu’un filtre, car elle vous permet d’accéder :
• à plusieurs tables à la fois (ce que l’on appelle une “jointure” en langage SQL) ;
• à un sous-ensemble de lignes et de colonnes dans les tables sous-jacentes. Cela
vous évite de renvoyer systématiquement toutes les lignes et colonnes,
améliore les performances et renforce la sécurité. La mémoire n’est pas perdue
dans le traitement de données inutiles et vous pouvez faire en sorte que
l’utilisateur ne puisse pas visualiser ou modifier certains champs.
Les requêtes peuvent être statiques ou bien contenir des paramètres susceptibles
de changer. Les requêtes utilisant des paramètres sont appelées des requêtes
paramétrées. Les valeurs affectées aux paramètres sont insérées dans la requête
par le BDE avant qu’elle ne soit exécutée. Les requêtes paramétrées sont d’une
utilisation très souple, car elles permettent en mode exécution de changer à la
volée ce que l’utilisateur voit et les données auxquelles il accède sans devoir
modifier l’instruction SQL.

21-2 Guide du développeur


Pour une utilisation efficace des requêtes

Généralement, les requêtes sont utilisées pour sélectionner les données visibles
par l’utilisateur dans l’application. Elles permettent d’effectuer des opérations de
mise à jour, d’insertion et de suppression. Lorsque vous utilisez une requête
pour effectuer de telles opérations, elle ne renvoie pas les enregistrements à
l’écran (ce qui n’est pas le cas avec les tables).
Pour plus d’informations sur l’utilisation de la propriété SQL et l’écriture
d’instructions SQL, voir “Spécification de l’instruction SQL à exécuter” à la
page 21-6. Pour plus d’informations sur l’utilisation de paramètres dans les
instructions SQL, voir “Définition de paramètres” à la page 21-9. Pour plus
d’informations sur l’exécution d’une requête, voir “Exécution d’une requête” à la
page 21-13.

Informations pour les développeurs d’applications sur serveur


En tant que développeur d’applications sur serveur, vous devez être familiarisé
avec le langage SQL et les fonctionnalités de votre serveur de base de données.
Pour vous, une requête est l’instruction SQL que vous utilisez pour accéder aux
données. Vous savez comment manipuler cette instruction et ses paramètres
facultatifs.
L’instruction SQL et ses paramètres sont les éléments les plus importants d’un
composant requête. La propriété SQL du composant requête est utilisée pour
fournir l’instruction SQL nécessaire pour accéder aux données. La propriété
Params du composant est un tableau de paramètres facultatifs à lier dans votre
requête. Toutefois, un composant requête dépasse largement l’envergure d’une
instruction SQL et ses paramètres. C’est aussi l’interface entre votre application
client et le BDE.
L’application client utilise les propriétés et les méthodes des composants requête
pour manipuler les instructions SQL et leurs paramètres, pour spécifier les bases
de données à interroger, pour préparer les requêtes avec des paramètres et les
exécuter. Les méthodes d’un composant requête appellent le BDE, qui à son tour
traite les demandes de votre requête, puis communique avec le serveur de base
de données par l’intermédiaire d’un pilote SQL Links. Le cas échéant, le serveur
retransmet un ensemble de résultats au BDE et celui-ci le renvoie à votre
application par l’intermédiaire du composant requête.
Lorsque vous travaillez avec un composant requête, vous devez savoir que les
termes utilisés pour décrire les fonctions BDE peuvent avoir un autre sens qu’en
programmation SQL. Par exemple, dans le Moteur de base de données Borland,
le terme “alias” désigne le nom raccourci relatif au chemin d’accès sur le serveur
de base de données. L’alias BDE est stocké dans un fichier de configuration et
est défini dans la propriété DatabaseName du composant requête. Vous pouvez
cependant utiliser les alias de table, ou "noms de corrélation de table", dans vos
instructions SQL.
Remarque La terminologie BDE est utilisée dans ce chapitre, car elle est reprise dans toute
la documentation Borland. Cependant, à chaque fois qu’il y a un risque de
confusion, des explications sont fournies.

Manipulation des requêtes 21-3


Bases de données accessibles par un composant requête

Pour en savoir plus sur la propriété SQL et l’écriture d’instructions SQL, voir
“Spécification de l’instruction SQL à exécuter” à la page 21-6. Pour plus
d’informations sur l’utilisation de paramètres dans les instructions SQL, voir
“Définition de paramètres” à la page 21-9. Pour plus d’informations sur la
préparation d’une requête, voir “Préparation d’une requête” à la page 21-15, et
pour plus d’informations sur son exécution, voir “Exécution d’une requête” à la
page 21-13.

Bases de données accessibles par un composant requête


Le composant TQuery vous permet d’accéder à des données qui se trouvent :
• Dans des tables Paradox ou dBASE en utilisant SQL local (il fait partie du
BDE). SQL local est un sous-ensemble de la spécification SQL-92. La majeure
partie de la syntaxe DML et suffisamment de syntaxe DDL sont supportées
pour permettre la manipulation de ces types de tables. Reportez-vous à l’aide
de SQL local, LOCALSQL.HLP, pour plus d’informations sur la syntaxe SQL
supportée.
• Dans des bases de données de serveur InterBase local en utilisant le moteur
InterBase. Pour plus d’informations sur le support de la syntaxe SQL du
standard SQL-92 d’InterBase et sur le support de la syntaxe étendue, voir le
Guide de référence du langage InterBase.
• Dans des bases de données de serveur de base de données comme Oracle,
Sybase, MS-SQL Server, Informix, DB2 et InterBase (selon votre version de
Delphi). Pour être en mesure d’accéder à un serveur distant, le pilote SQL
Link approprié et le logiciel client (fourni par le revendeur) propre au serveur
de bases de données doivent être installés. Toute syntaxe SQL standard
supportée par ces serveurs est autorisée. Pour plus d’informations sur la
syntaxe SQL, ses limitations et ses extensions, voir la documentation de votre
serveur.
Delphi supporte également les requêtes hétérogènes lancées sur plusieurs
serveurs ou types de table (comme par exemple, une table Oracle et une table
Paradox). Lorsque vous créez une requête hétérogène, le BDE utilise SQL local
pour traiter la requête. Pour plus d’informations, voir “Création de requêtes
hétérogènes” à la page 21-16.

Utilisation d’un composant requête


Pour utiliser un composant requête dans une application, suivez les étapes ci-
dessous lors de la phase de conception :
1 Depuis l’onglet AccèsBD de la palette des composants, placez un composant
requête dans un module de données et définissez sa propriété Name en
fonction des besoins de votre application.

21-4 Guide du développeur


Utilisation d’un composant requête

2 Donnez à la propriété DatabaseName du composant le nom de la base de


données à interroger. DatabaseName peut être un alias BDE, un chemin de
répertoire explicite (pour les tables locales) ou la valeur de la propriété
DatabaseName d’un composant TDatabase dans l’application.
3 Spécifiez une instruction SQL dans la propriété SQL du composant et, le cas
échéant, spécifiez les paramètres de l’instruction dans la propriété Params.
Pour plus de détails, reportez-vous à “Spécification de la propriété SQL en
phase de conception” à la page 21-7.
4 Si les données de la requête doivent être utilisées avec des contrôles de données
visuels, depuis l’onglet AccèsBD de la palette des composants, placez un
composant source de données dans le module de données et donnez à sa
propriété DataSet le nom du composant requête. Le composant source de données
sert à renvoyer le résultat de la requête (aussi appelé l’ensemble de résultats) dans
des composants orientés données. Connectez les composants orientés données à
la source de données à l’aide des propriétés DataSource et DataField.
5 Activez le composant requête. Pour les requêtes renvoyant un ensemble de
résultats, utilisez la propriété Active ou la méthode Open. Pour les requêtes qui
se limitent à une action sur une table et ne renvoient pas d’ensemble de
résultats, utilisez la méthode ExecSQL.
Suivez les étapes ci-dessous pour exécuter une requête pour la première fois en
phase exécution :
1 Fermez le composant requête.
2 Spécifiez une instruction SQL dans la propriété SQL, soit parce qu’elle n’a pas
été définie en mode conception, soit parce que vous voulez la changer. Si vous
souhaitez conserver l’instruction SQL spécifiée lors de la conception, passez à
l’étape suivante. Pour plus d’informations sur la définition de la propriété
SQL, voir “Spécification de l’instruction SQL à exécuter” à la page 21-6.
3 Définissez les paramètres et leurs valeurs dans la propriété Params. Vous
pouvez les spécifier directement ou utiliser la méthode ParamByName. Si une
requête ne contient pas de paramètre ou si les paramètres définis lors de la
conception restent les mêmes, passez à l’étape suivante. Pour plus
d’informations sur la définition des paramètres, voir “Définition de
paramètres” à la page 21-9.
4 Appelez la méthode Prepare pour initialiser le BDE et lier les valeurs des
paramètres dans la requête. Bien que cette étape soit facultative, il est
fortement recommandé de la suivre. Pour plus d’informations sur la
préparation d’une requête, voir “Préparation d’une requête” à la page 21-15.
5 Si votre requête renvoie un ensemble de résultats, appelez la méthode Open ;
sinon appelez ExecSQL. Pour plus d’informations sur l’ouverture et l’exécution
d’une requête, voir “Exécution d’une requête” à la page 21-13.
Après avoir exécuté une requête pour la première fois et tant que vous ne
modifiez pas l’instruction SQL, l’application peut fermer et ouvrir une requête ou
l’exécuter de façon répétitive sans qu’il soit nécessaire de la préparer. Pour plus
d’informations sur la réutilisation des requêtes, voir “Exécution d’une requête” à
la page 21-13.

Manipulation des requêtes 21-5


Spécification de l’instruction SQL à exécuter

Spécification de l’instruction SQL à exécuter


La propriété SQL sert à spécifier l’instruction de la requête SQL à exécuter. En
phase de conception, une requête est automatiquement préparée et exécutée si la
propriété Active du composant requête vaut True. A l’exécution, une requête est
préparée par un appel à la méthode Prepare, et est exécutée lorsque l’application
appelle la méthode Open ou ExecSQL du composant.
La propriété SQL est un objet TStrings. C’est donc un tableau de chaînes texte et
un ensemble de propriétés, événements et méthodes qui les manipulent. Les
chaînes spécifiées dans la propriété SQL sont automatiquement concaténées pour
produire l’instruction SQL à exécuter. L’instruction peut être spécifiée dans
plusieurs chaînes séparées. L’un des avantages d’utiliser une série de chaînes
réside dans la possibilité de diviser l’instruction SQL en unités logiques (comme
placer la clause WHERE d’une instruction SELECT dans sa propre chaîne). Il est
ainsi plus facile de modifier et déboguer la requête.
L’instruction SQL peut être une requête qui contient des valeurs et des noms de
champs codés en dur, ou être une requête paramétrée qui contient des
paramètres modifiables correspondant à des valeurs de champ devant être liées
dans l’instruction avant son exécution. L’instruction suivante, par exemple, est
codée en dur :
SELECT * FROM Customer WHERE CustNo = 1231
Les instructions codées en dur sont utiles lorsque des applications exécutent des
requêtes exactes et connues à chaque exécution. En phase de conception ou
d’exécution, on peut facilement remplacer une requête codée en dur par une
autre requête codée en dur ou paramétrée. A chaque fois que la propriété SQL
est modifiée, la requête est automatiquement fermée et sa préparation est
réinitialisée.
Remarque Dans les requêtes exécutées sur le moteur BDE en utilisant SQL local, vous
devez placer entre guillemets les noms de colonne contenant des espaces ou des
caractères spéciaux et les faire précéder du nom de la table et d’un point, comme
BIOLIFE.”Species Name”. Pour plus d’informations sur les noms de colonnes
corrects, voir la documentation de votre serveur.
Une requête paramétrée contient un ou plusieurs paramètres réservés (des
variables d’applications correspondant à des valeurs de comparaison comme
celles trouvées dans la clause WHERE d’une instruction SELECT). Grâce aux
requêtes paramétrées, il est possible de modifier une valeur sans réécrire
l’application. Pour être en mesure d’exécuter une instruction SQL pour la
première fois, les valeurs de paramètres doivent être liées dans l’instruction. Cela
est fait automatiquement même si vous n’appelez pas explicitement la méthode
Prepare avant d’exécuter une requête.
Cette instruction est une requête paramétrée :
SELECT * FROM Customer WHERE CustNo = :Number

21-6 Guide du développeur


Spécification de l’instruction SQL à exécuter

La variable Number indiquée par le signe deux-points est un paramètre


représentant une valeur de comparaison devant être fournie à l’exécution et
pouvant varier pour chaque exécution de l’instruction. La valeur réelle de
Number est spécifiée dans la propriété Params du composant requête.
Astuce La spécification de noms de variables pour des paramètres correspondant à des
noms de colonnes réels est un procédé recommandé. Prenons l’exemple d’une
colonne intitulée “Number” ; le paramètre lui correspondant devra s’appeler
“:Number”. En utilisant des noms qui se correspondent, vous garantissez que si
la requête utilise sa propriété DataSource pour définir les valeurs des paramètres,
les noms de variables pourront correspondre à des noms de champs corrects.

Spécification de la propriété SQL en phase de conception


Vous pouvez spécifier la propriété SQL au moment de la conception à l’aide de
l’éditeur de listes de chaînes. Pour ce faire :
• double-cliquez sur la colonne de valeur de la propriété SQL
ou ;
• cliquez sur le bouton à points de suspension.
Le nombre de lignes d’une instruction SQL importe peu. Cependant, une
instruction répartie sur plusieurs lignes est plus facile à lire, à modifier et à
déboguer. Choisissez OK pour affecter à la propriété SQL le texte saisi.
En principe, la propriété SQL ne peut contenir qu’une instruction SQL complète
à la fois. Toutefois, les instructions peuvent être très complexes (ainsi une
instruction SELECT peut comporter une clause WHERE utilisant plusieurs
opérateurs logiques comme AND et OR). Certains serveurs supportent la syntaxe
“batch”, qui permet les instructions multiples ; si votre serveur la prend en
charge, vous pouvez entrer plusieurs instructions dans la propriété SQL.
Remarque Avec certaines version de Delphi, vous pouvez également utiliser le constructeur
SQL pour créer une instruction SQL basée sur une représentation visible de
tables et de champs d’une base de données. Pour ce faire, sélectionnez un
composant requête, cliquez dessus avec le bouton droit de la souris pour ouvrir
le menu contextuel et choisissez Editeur de requête graphique. Pour plus
d’informations sur l’utilisation du constructeur SQL, ouvrez-le et consultez son
aide en ligne.

Spécification d’une instruction SQL à l’exécution


La propriété SQL peut être définie de trois façons à l’exécution. Une application
peut définir directement la propriété SQL, elle peut appeler la méthode
LoadFromFile de la propriété SQL pour lire une instruction SQL dans un fichier,
ou une instruction SQL dans un objet liste de chaînes peut être affectée à la
propriété SQL.

Manipulation des requêtes 21-7


Spécification de l’instruction SQL à exécuter

Définition directe de la propriété SQL


Pour définir directement la propriété SQL lors de l’exécution :
1 Appelez la méthode Close pour désactiver la requête. Même si une tentative
de modification de la propriété SQL désactive automatiquement la requête, il
est préférable de la désactiver de façon explicite.
2 Si vous remplacez la totalité de l’instruction SQL, appelez la méthode Clear
pour que la propriété SQL supprime l’instruction SQL en cours.
3 Si vous construisez la totalité de l’instruction SQL à partir d’aucun élément ou
ajoutez une ligne à une instruction existante, appelez la méthode Add pour
qu’une ou plusieurs chaînes puissent être ajoutées afin de créer une nouvelle
instruction SQL. Si vous modifiez une ligne existante, utilisez la propriété SQL
avec un index pour indiquer la ligne concernée et affectez la nouvelle valeur.
4 Appelez la méthode Open ou ExecSQL pour exécuter la requête.
Le code suivant illustre la construction d’une instruction SQL entière à partir
d’aucun élément.
with CustomerQuery do begin
Close; { Ferme la requête si elle est
active }
with SQL do begin
Clear; { Supprime l’actuelle instruction SQL s’il y
en a une }
Add(‘SELECT * FROM Customer’); { ajoute la première ligne
SQL... }
Add(‘WHERE Company = “Sight Diver”’); { ... puis la seconde }
end;
Open; { active la requête }
end;
Le code suivant illustre la modification d’une seule ligne d’une instruction SQL
existante. Dans ce cas, la clause WHERE existe déjà sur la seconde ligne de
l’instruction. Elle est référencée via la propriété SQL à l’aide d’un index de valeur 1.
CustomerQuery.SQL[1] := ‘WHERE Company = “Kauai Dive Shoppe“’;
Remarque Si une requête utilise des paramètres, vous devez également définir leurs valeurs
initiales et appeler la méthode Prepare avant d’ouvrir ou d’exécuter la requête.
L’appel explicite de Prepare se justifie si la même instruction SQL est
fréquemment utilisée ; sinon elle est automatiquement appelée par le composant
requête.

Chargement de la propriété SQL depuis un fichier


Vous pouvez également utiliser la méthode LoadFromFile pour affecter une
instruction SQL d’un fichier texte à la propriété SQL. La méthode LoadFromFile
efface automatiquement le contenu de la propriété SQL avant de charger la
nouvelle instruction depuis le fichier. Exemple :
CustomerQuery.Close;
CustomerQuery.SQL.LoadFromFile(‘c:\orders.txt’);
CustomerQuery.Open;

21-8 Guide du développeur


Définition de paramètres

Remarque Si l’instruction SQL contenue dans le fichier est une requête paramétrée,
définissez les valeurs initiales des paramètres et appelez la méthode Prepare
avant d’ouvrir ou d’exécuter la requête. L’appel explicite de Prepare se justifie si
la même instruction SQL est fréquemment utilisée ; sinon elle est
automatiquement appelée par le composant requête.

Chargement de la propriété SQL depuis un objet liste de chaînes


Vous pouvez aussi utiliser la méthode Assign de la propriété SQL pour copier le
contenu d’un objet liste de chaînes dans la propriété SQL. La méthode Assign
efface automatiquement le contenu de la propriété SQL avant de copier la
nouvelle instruction. Par exemple, voici la copie d’une instruction SQL depuis un
composant TMemo :
CustomerQuery.Close;
CustomerQuery.SQL.Assign(Memo1.Lines);
CustomerQuery.Open;
Remarque Si l’instruction SQL est une requête paramétrée, définissez les valeurs initiales des
paramètres et appelez la méthode Prepare avant d’ouvrir ou d’exécuter la requête.
L’appel explicite de Prepare se justifie si la même instruction SQL est fréquemment
utilisée ; sinon elle est automatiquement appelée par le composant requête.

Définition de paramètres
Une instruction SQL paramétrée contient des paramètres, ou variables,
susceptibles de changer pendant la conception ou l’exécution. Ils peuvent
remplacer des valeurs de données, comme celles utilisées dans les clauses WHERE
pour les comparaisons. En règle générale, les paramètres sont utilisés pour
représenter des valeurs transmises à l’instruction. Par exemple, dans l’instruction
INSERT ci-dessous, les valeurs à insérer sont transmises en tant que paramètres :
INSERT INTO Country (Name, Capital, Population)
VALUES (:Name, :Capital, :Population)
Dans cette instruction SQL, :name, :capital et :population représentent les valeurs
réelles fournies à l’instruction par votre application au moment de l’exécution.
Pour la première exécution d’une requête paramétrée, votre application doit
appeler la méthode Prepare pour lier les valeurs en cours des paramètres dans
l’instruction SQL. Cette liaison permet au BDE et au serveur d’allouer des
ressources à l’instruction et à ses paramètres afin d’accélérer la vitesse
d’exécution de la requête.
with Query1 do begin
Close;
Unprepare;
ParamByName(‘Name’).AsString := ‘Belize’;
ParamByName(‘Capital’).AsString := ‘Belmopan’;
ParamByName(‘Population’).AsInteger := ‘240000’;
Prepare;
Open;
end;

Manipulation des requêtes 21-9


Définition de paramètres

Attribution de paramètres en phase de conception


Au moment de la conception, les paramètres de l’instruction SQL apparaissent
dans l’éditeur de collection de paramètres. Pour accéder aux objets TParam des
paramètres, appelez l’éditeur de collection de paramètres, sélectionnez un
paramètre et accédez aux propriétés TParam dans l’inspecteur d’objets. Si
l’instruction SQL ne contient pas de paramètres, aucun objet TParam ne figure
dans l’éditeur de collection. Vous pouvez uniquement ajouter des paramètres en
les écrivant dans l’instruction SQL.
Pour accéder aux paramètres :
1 Sélectionnez le composant requête.
2 Cliquez sur le bouton à points de suspension de la propriété Params dans
l’inspecteur d’objets.
3 Dans l’éditeur de collection de paramètres, sélectionnez un paramètre.
4 L’objet TParam du paramètre sélectionné apparaît dans l’inspecteur d’objets.
5 Inspectez et modifiez les propriétés de l’objet TParam dans l’inspecteur
d’objets.
Pour les requêtes ne contenant pas de paramètres (la propriété SQL est vide ou
l’instruction SQL existante n’a pas de paramètres), la boîte de dialogue de
l’éditeur de collection ne présente aucun paramètre. Si des paramètres sont déjà
définis pour une requête, l’éditeur de paramètres présente tous les paramètres
existants.
Remarque Le composant TQuery partage l’objet TParam et son éditeur de collection avec
différents composants. Bien que le menu contextuel de l’éditeur de collection
accessible par le bouton droit de la souris contienne toujours les options Ajouter
et Supprimer, celles-ci ne sont jamais activées pour les paramètres TQuery. La
seule façon d’ajouter ou de supprimer des paramètres TQuery consiste à utiliser
l’instruction SQL.
Chaque fois qu’un paramètre est sélectionné dans l’éditeur de collection,
l’inspecteur d’objets affiche les propriétés et les événements correspondants.
Définissez les valeurs des propriétés et des méthodes des paramètres dans
l’inspecteur d’objets.
La propriété DataType donne la liste des types de données BDE possibles pour le
paramètre sélectionné dans la boîte de dialogue d’édition. Initialement, le type
est ftUnknown. Vous devez définir un type pour chaque paramètre.
Généralement, les types de données BDE sont conformes aux types de données
des serveurs. Pour plus d’informations sur les correspondances de types de
données par rapport aux serveurs, voir l’aide du BDE (\Borland\Common Files\
BDE\BDE32.HLP).
La propriété ParamType donne le type de paramètre sélectionné dans la boîte de
dialogue d’édition. Initialement, le type est ptUnknown. Vous devez définir un
type pour chaque paramètre.

21-10 Guide du développeur


Définition de paramètres

Utilisez la propriété Value pour spécifier une valeur pour le paramètre sélectionné
lors de la conception. Cette procédure n’est pas obligatoire lorsque les valeurs des
paramètres sont fournies en phase d’exécution ; laissez alors la propriété Value vide.

Affectation de paramètres en phase d’exécution


Pour créer des paramètres lors de l’exécution, utilisez les possibilités suivantes :
• La méthode ParamByName permet d’affecter des valeurs à un paramètre en se
basant sur son nom.
• La propriété Params permet d’affecter des valeurs à un paramètre en se basant
sur sa position ordinale dans l’instruction SQL.
• La propriété Params.ParamValues permet d’affecter des valeurs à un paramètre
ou à plusieurs paramètres dans une seule ligne de commande en se basant sur
le nom de chaque paramètre défini. Cette méthode utilise des variants et évite
d’avoir recours à des valeurs transtypées.
Dans tous les exemples suivants, on suppose que la propriété SQL contient
l’instruction SQL suivante. Les trois paramètres utilisés ont un type de données
ftString.
INSERT INTO "COUNTRY.DB"
(Name, Capital, Continent)
VALUES (:Name, :Capital, :Continent)
Le code suivant utilise la méthode ParamByName et affecte le texte d’une boîte de
saisie au paramètre Capital :
Query1.ParamByName(‘Capital’).AsString := Edit1.Text;
Le même code peut être réécrit en utilisant la propriété Params et un index de
valeur 1 (on suppose que le paramètre Capital est le second paramètre dans
l’instruction SQL) :
Query1.Params[1].AsString := Edit1.Text;
La ligne de commande suivante définit les trois paramètres en même temps avec
la propriété Params.ParamValues :
Query1.Params.ParamValues[‘Country;Capital;Continent’] :=
VarArrayOf([Edit1.Text, Edit2.Text, Edit3.Text]);

Utilisation d’une source de données pour lier les paramètres


Si les valeurs de paramètres d’une requête paramétrée ne sont pas liées pendant
la phase de conception ou spécifiées à l’exécution, le composant requête tente de
fournir des valeurs d’après sa propriété DataSource. DataSource spécifie un
composant requête ou table différent permettant au composant requête de
rechercher des noms de champs correspondant au nom des paramètres non liés.
Cet ensemble de données de recherche doit être créé et défini avant de créer le
composant requête qui l’utilisera. S’il trouve des correspondances, le composant
requête lie les valeurs des paramètres à celles des champs de l’enregistrement en
cours indiqué par la source de données.

Manipulation des requêtes 21-11


Définition de paramètres

La création d’une application simple peut vous permettre de comprendre


comment la propriété DataSource permet de lier une requête dans une fiche
maître-détail. Supposons l’existence d’un module de données appelé LinkModule
et contenant un composant requête appelé OrdersQuery dont la propriété SQL est
définie comme suit :
SELECT CustNo, OrderNo, SaleDate
FROM Orders
WHERE CustNo = :CustNo
Le module de données LinkModule contient également :
• Un composant ensemble de données TTable appelé CustomersTable lié à la table
CUSTOMER.DB.
• Un composant TDataSource appelé OrdersSource. La propriété DataSet de
OrdersSource pointe sur OrdersQuery.
• Un composant TDataSource appelé CustomersSource lié à CustomersTable. La
propriété DataSource du composant OrdersQuery a également la valeur
CustomersSource. C’est elle qui fait d’OrdersQuery une requête liée.
Supposons également que cette application comporte une fiche appelée Requête
liée qui contient deux grilles de données, une grille Customers Table liée à
CustomersSource et une grille Order Query liée à OrdersSource.
La figure suivante montre comment se présente cette application au moment de
la conception.
Figure 21.1 Fiche requête maître-détail et module de données en phase de conception

Remarque Si vous construisez cette application, créez le composant table et sa source de


données avant le composant requête.

21-12 Guide du développeur


Exécution d’une requête

Si vous compilez cette application, il n’est pas attribué de valeur au paramètre


:CustNo de l’instruction SQL de OrdersQuery au moment de l’exécution.
OrdersQuery lui cherche alors une correspondance d’après son nom dans une
colonne de table indiquée par CustomersSource. CustomersSource obtient ses
données de CustomersTable qui, de son côté, dérive ses données de la table
CUSTOMER.DB. Comme celle-ci contient une colonne appelée CustNo, la valeur
du champ CustNo dans l’enregistrement en cours de l’ensemble de données
CustomersTable est affectée au paramètre :CustNo de l’instruction SQL
OrdersQuery. Les grilles sont liées dans le cadre d’une relation maître-détail. Au
moment de l’exécution, à chaque fois que vous sélectionnez un enregistrement
différent dans la grille Customers Table, l’instruction SELECT de OrdersQuery
extrait toutes les commandes basées sur le numéro de client en cours.

Exécution d’une requête


Quand l’instruction SQL de la propriété SQL et les paramètres de la requête sont
définis, vous pouvez exécuter la requête. Lorsqu’une requête est exécutée, le BDE
reçoit et traite des instructions SQL provenant de votre application. Si la requête
porte sur des tables locales (dBASE et Paradox), le moteur SQL du BDE traite
l’instruction SQL et, pour une requête SELECT, renvoie les données à
l’application. Si la requête porte sur une table de serveur SQL et que la propriété
TQuery.RequestLive vaut False, l’instruction SQL n’est pas traitée ni interprétée par
le BDE, mais transmise directement au système de bases de données. Si la
propriété RequestLive vaut True, l’instruction SQL doit se conformer aux
standards SQL locaux afin que le BDE puisse l’utiliser pour répercuter les
modifications de données dans la table.
Remarque Avant d’exécuter une requête pour la première fois, vous devez appeler la
méthode Prepare pour augmenter ses performances. La préparation a pour effet
d’initialiser le BDE et le serveur de base de données, chacun allouant des
ressources système à la requête. Pour plus d’informations sur la préparation
d’une requête, voir “Préparation d’une requête” à la page 21-15.
Les sections suivantes expliquent comment exécuter des instructions SQL
statiques et dynamiques en phase de conception et d’exécution.

Exécution d’une requête pendant la conception


Pour exécuter une requête en phase de conception, mettez sa propriété Active à
True dans l’inspecteur d’objets.
Le résultat de la requête, s’il y en a un, est affiché dans un contrôle orienté
données associé au composant requête.
Remarque La propriété Active ne peut être utilisée qu’avec des requêtes qui renvoient un
ensemble de résultats, à l’image de l’instruction SELECT.

Manipulation des requêtes 21-13


Exécution d’une requête

Exécution d’une requête pendant l’exécution


Pour exécuter une requête à l’exécution, utilisez l’une des méthodes suivantes :
• Open exécute une requête qui renvoie un ensemble de résultats, à l’image de
l’instruction SELECT.
• ExecSQL exécute une requête qui ne renvoie pas d’ensemble de résultats, à
l’image des instructions INSERT, UPDATE et DELETE.
Remarque Si, lors de la conception, vous ne savez pas si une requête renverra un
ensemble de résultats au moment de son exécution, codez ses deux types
d’instructions d’exécution dans un bloc try..except. Placez un appel à la
méthode Open dans la clause try. Cela vous permet de supprimer le message
d’erreur qui apparaîtrait suite à l’utilisation d’une méthode d’activation
inapplicable au type d’instruction SQL utilisé. Vérifiez le type d’exception qui
survient. S’il s’agit d’une exception autre que ENoResult, l’exception a une autre
cause et elle doit être traitée. Cela fonctionne car une requête action est
exécutée lorsque la requête est activée avec la méthode Open, mais une
exception se produit en plus.
try
Query2.Open;
except
on E: Exception do
if not (E is ENoResultSet) then
raise;
end;

Exécution d’une requête renvoyant un ensemble de résultats


Pour exécuter une requête renvoyant un ensemble de résultats (une requête qui
utilise une instruction SELECT), procédez comme suit :
1 Appelez la méthode Close pour vous assurer que la requête n’est pas déjà
ouverte. Si la requête est ouverte, vous ne pouvez pas l’ouvrir sans d’abord la
fermer. Le fait de fermer une requête et de la ré-ouvrir provoque l’envoi de
nouvelles données du serveur.
2 Appelez la méthode Open pour exécuter la requête.
Exemple :
CustomerQuery.Close;
CustomerQuery.Open; { Requête qui renvoie un ensemble de résultats }
Pour plus d’informations sur la navigation à l’intérieur d’un ensemble de
résultats, voir “Désactivation des curseurs bidirectionnels” à la page 21-17. Pour
plus d’informations sur l’édition et la mise à jour d’un ensemble de résultats,
voir “Manipulation des ensembles de résultats” à la page 21-17.

21-14 Guide du développeur


Préparation d’une requête

Exécution d’une requête sans ensemble de résultats


Pour exécuter une requête qui ne renvoie pas d’ensemble de résultats (une
requête qui possède une instruction SQL comme INSERT, UPDATE ou DELETE),
appelez ExecSQL.
Exemple :
CustomerQuery.ExecSQL; { La requête ne renvoie pas d’ensemble de résultats }

Préparation d’une requête


La préparation d’une requête est une étape facultative qui précède l’exécution de
la requête. Elle a pour effet d’envoyer au BDE une instruction SQL et ses
paramètres, pour analyse syntaxique, affectation de ressources et optimisation. Le
BDE, à son tour, notifie au serveur de base de données de se préparer pour
l’interrogation. Le serveur, lui aussi, peut allouer des ressources à la requête. Ces
opérations peuvent augmenter les performances de la requête et accélérer votre
application, tout particulièrement lorsqu’on travaille avec des requêtes
modifiables.
Une application peut préparer une requête en appelant la méthode Prepare. Si
vous n’avez pas préparé la requête avant de l’exécuter, Delphi le fait pour vous
à chaque fois que la méthode Open ou ExecSQL est appelée. Même si Delphi
prépare les requêtes à votre place, il est préférable de les préparer explicitement.
Cela permet d’avoir du code auto-documentant et d’exprimer clairement vos
intentions. Exemple :
CustomerQuery.Close;
if not (CustomerQuery.Prepared) then
CustomerQuery.Prepare;
CustomerQuery.Open;
Cet exemple vérifie la propriété Prepared du composant requête afin de
déterminer si la requête a déjà été préparée. Prepared est une valeur booléenne
qui vaut True si une requête est déjà préparée. Sinon, la méthode Prepare est
appelée avant Open.

Réinitialisation de la préparation d’une requête


La méthode UnPrepare met la propriété Prepared à false. Unprepare présente les
caractéristiques suivantes ;
• Elle garantit que la propriété SQL est préparée une nouvelle fois avant
l’exécution de la requête.
• Elle informe le BDE qu’il doit libérer les ressources internes allouées à
l’instruction.
• Elle informe le serveur de libérer les ressources allouées à l’instruction.

Manipulation des requêtes 21-15


Création de requêtes hétérogènes

Pour réinitialiser la préparation d’une requête, utilisez le code suivant :


CustomerQuery.UnPrepare;
Remarque Lorsque vous modifiez le texte de la propriété SQL d’une requête, le composant
requête ferme automatiquement la requête et réinitialise sa préparation.

Création de requêtes hétérogènes


Delphi prend en charge les requêtes hétérogènes, c’est-à-dire les requêtes portant
sur des tables réparties sur plusieurs bases de données. Une requête hétérogène
peut joindre des tables stockées sur différents serveurs, voire sur différents types
de serveurs. Ainsi, une requête hétérogène pourra porter sur une base de
données Oracle, une table d’une base de données Sybase et sur une table locale
dBASE. Lorsque vous exécutez une requête hétérogène, le BDE analyse et traite
la requête en utilisant SQL local (la syntaxe SQL étendue, propre au serveur
n’est donc pas supportée).
Pour effectuer une requête hétérogène, suivez les étapes ci-dessous :
1 Définissez des alias BDE séparés pour chaque base de données interrogée par
la requête. Laissez vide la propriété DatabaseName de TQuery ; les noms des
deux bases de données utilisées seront spécifiés dans l’instruction SQL.
2 Spécifiez dans la propriété SQL l’instruction SQL à exécuter. Faites précéder
chaque nom de table par l’alias BDE correspondant à la base de données où
se trouve cette table. La référence à la table est précédée du nom de l’alias
BDE, entre deux-points. L’ensemble de la référence est placée entre guillemets.
3 Dans la propriété Params, définissez éventuellement les paramètres de la
requête.
4 Appelez la méthode Prepare pour préparer la requête avant sa première
exécution.
5 Selon le type de requête que vous voulez exécuter, appelez Open ou ExecSQL.
Supposons que vous définissiez un alias appelé Oracle1 pour une base de
données Oracle contenant la table CUSTOMER et un alias Sybase1 pour une base
Sybase contenant une table ORDERS. Une requête simple effectuée sur ces deux
tables se présentera comme suit :
SELECT Customer.CustNo, Orders.OrderNo
FROM ”:Oracle1:CUSTOMER”
JOIN ”:Sybase1:ORDERS”
ON (Customer.CustNo = Orders.CustNo)
WHERE (Customer.CustNo = 1503)
Au lieu d’utiliser un alias BDE pour spécifier la base de données dans une
requête hétérogène, vous pouvez utiliser un composant TDatabase. Configurez
normalement le composant TDatabase pour pointer sur la base de données,
attribuez à TDatabase.DatabaseName une valeur quelconque mais unique puis
utilisez cette valeur dans l’instruction SQL au lieu du nom d’un alias BDE.

21-16 Guide du développeur


Amélioration des performances d’une requête

Amélioration des performances d’une requête


Certaines étapes permettent d’accélérer la vitesse d’exécution d’une requête :
• Vous pouvez donner à la propriété UniDirectional d’une requête la valeur True
si vous n’avez pas besoin de revenir en arrière dans un ensemble de résultats
(SQL-92 ne supporte pas cette fonctionnalité). Par défaut, UniDirectional est à
false car le BDE prend en charge les curseurs bidirectionnels.
• Vous pouvez préparer la requête avant son exécution. C’est particulièrement
utile si vous comptez exécuter une même requête plusieurs fois. Vous n’avez
à la préparer qu’une seule fois avant sa première utilisation. Pour en savoir
plus sur la préparation des requêtes, reportez-vous à “Préparation d’une
requête” à la page 21-15.

Désactivation des curseurs bidirectionnels


La propriété UniDirectional détermine si les curseurs BDE bidirectionnels sont
activés ou non pour une requête. Quand une requête renvoie un ensemble de
résultats, elle reçoit également un curseur, ou pointeur, placé sur le premier
enregistrement de cet ensemble. L’enregistrement en cours est celui dont les
valeurs de champ s’affichent dans les composants orientés données associés à la
source de données de l’ensemble de résultats.
Par défaut, UniDirectional est à false, ce qui signifie que le curseur d’un ensemble
de résultats peut se déplacer en avant et en arrière, indifféremment. La prise en
charge du curseur bidirectionnel nécessite un traitement plus lourd et risque de
ralentir certaines requêtes. Pour améliorer les performances, vous pouvez donner
à UniDirectional la valeur True ; le curseur ne pourra ainsi se déplacer que vers
l’avant dans un ensemble de résultats.
Si vous n’avez pas besoin de revenir en arrière dans un ensemble de résultats,
vous pouvez donner à UniDirectional la valeur True. Vous devez le faire avant de
préparer et d’exécuter la requête. Le code ci-dessous en est un exemple :
if not (CustomerQuery.Prepared) then begin
CustomerQuery.UniDirectional := True;
CustomerQuery.Prepare;
end;
CustomerQuery.Open; { Renvoie un ensemble de résultats avec un curseur unidirectionnel }

Manipulation des ensembles de résultats


Par défaut, l’ensemble de résultats renvoyé par une requête n’est accessible qu’en
lecture. Votre application peut afficher les valeurs des champs de cet ensemble
dans des contrôles orientés données, mais l’utilisateur n’est pas en mesure de les
éditer. Pour activer l’édition d’un ensemble de résultats, votre application doit
requérir un ensemble de résultats modifiable.

Manipulation des requêtes 21-17


Manipulation des ensembles de résultats

Activation de l’édition d’un ensemble de résultats


Pour demander un ensemble de résultats que les utilisateurs puissent éditer dans
les contrôles orientés données, donnez à la propriété RequestLive du composant
requête la valeur True. Toutefois, cela ne garantit pas l’obtention d’un ensemble
de résultats modifiable, mais le BDE essaie de répondre à la requête dans la
mesure de ses possibilités. En matière de requêtes sur des ensembles de résultats,
il existe certaines limitations selon que la requête utilise ou non l’analyseur SQL
local ou l’analyseur SQL du serveur. Les requêtes et les jointures hétérogènes
exécutées sur des tables Paradox ou dBASE sont analysées par le BDE par
l’intermédiaire de SQL local. Les requêtes exécutées sur des serveurs de base de
données distants sont, elles, analysées par le serveur.
Si une application demande et reçoit un ensemble de résultats modifiable, la
valeur True est attribuée à la propriété CanModify du composant requête.
Si une application requiert un ensemble de résultats modifiable, mais que la
syntaxe de l’instruction SELECT ne le permet pas, le BDE renvoie :
• soit un ensemble de résultats accessible en lecture seulement pour les requêtes
effectuées sous Paradox ou dBASE ;
• soit un code d’erreur pour les requêtes SQL sur serveur distant.

Utilisation de SQL local avec les ensembles de résultats


modifiables
Pour les requêtes utilisant l’analyseur SQL local, le BDE offre un support étendu
des ensembles de résultats modifiables, qu’il s’agisse de requêtes mono ou
multitables. L’analyseur SQL local est utilisé lorsqu’une requête est exécutée sur
une ou plusieurs tables dBASE ou Paradox, sur une ou plusieurs tables de
serveur distant si les noms des tables sont précédés d’un alias de base de
données BDE. Les sections suivantes présentent les restrictions relatives au
renvoi d’un ensemble de résultats modifiable.

Restrictions relatives aux requêtes modifiables


Pour qu’un ensemble de résultats modifiable puisse être renvoyé depuis une
seule table ou vue, la requête ne doit contenir aucun des éléments suivants :
• Une instruction DISTINCT dans la clause SELECT.
• Des jointures (internes, externes ou UNION).
• Des fonctions d’agrégat, avec ou sans les clauses GROUP BY ou HAVING.
• Des vues ou des tables de base ne pouvant être mises à jour.
• Des requêtes secondaires.
• Des clauses ORDER BY non basées sur un index.

21-18 Guide du développeur


Manipulation des ensembles de résultats

Utilisation de SQL sur serveur distant avec les ensembles de


résultats modifiables
Pour les requêtes utilisant le SQL direct (c’est-à-dire toutes les requêtes lancées
sur des serveurs de base de données distants), les ensembles de résultats
modifiables sont limités aux normes définies par SQL-92 et par toute autre
restriction due au serveur.
Pour qu’un ensemble de résultats modifiable puisse être renvoyé depuis une
seule table ou vue, la requête ne doit contenir aucun des éléments suivants :
• Une clause DISTINCT dans l’instruction SELECT.
• Des fonctions d’agrégat, avec ou sans les clauses GROUP BY ou HAVING.
• Des références à plusieurs tables de base de données ou à des vues pouvant
être mises à jour (c’est-à-dire des jointures).
• Des requêtes secondaires contenant des références à la table dans la clause
FROM ou à d’autres tables.

Restrictions sur la mise à jour d’un ensemble de résultats


modifiable
Si une requête renvoie un ensemble de résultats modifiable, il se peut que vous
ne puissiez pas mettre à jour directement l’ensemble de résultats s’il contient des
champs liés ou si vous avez permuté des index avant de tenter une mise à jour.
Si vous êtes confronté à de telles conditions, vous pourrez sans doute traiter
l’ensemble de résultats comme un ensemble en lecture seulement.

Mise à jour d’un ensemble de résultats en lecture seulement


Les applications peuvent mettre à jour les données renvoyées dans un ensemble
de résultats en lecture seulement si elles utilisent les modifications en mémoire
cache. Pour mettre à jour un résultat en lecture seulement associé à un
composant requête :
1 Ajoutez un composant TUpdateSQL au module de données de votre
application afin de pouvoir émettre les mises à jour dans un ensemble de
résultats en lecture seulement.
2 Entrez l’instruction de mise à jour SQL de l’ensemble de résultats dans les
propriétés ModifySQL, InsertSQL ou DeleteSQL du composant TUpdateSQL.
3 Donnez la valeur True à la propriété CachedUpdate du composant requête.
Pour plus d’informations sur les mises à jour en mémoire cache, reportez-vous
au chapitre 25, “Manipulation des mises à jour en mémoire cache”.

Manipulation des requêtes 21-19


21-20 Guide du développeur
Chapitre

Manipulation Chapter 22
22
des procédures stockées
Ce chapitre décrit comment utiliser les procédures stockées dans vos applications
de base de données. Une procédure stockée est un programme autonome écrit
dans le langage de la procédure et du déclencheur propre au système de base de
données utilisé. Il existe fondamentalement deux types de procédures stockées.
Le premier type extrait des données (comme dans le cas d’une requête SELECT).
Les données extraites peuvent se présenter sous la forme d’un ensemble de
données composé d’une ou plusieurs lignes de données, elles-mêmes composées
d’une ou plusieurs colonnes. Autre possibilité, les données extraites se présentent
sous la forme d’unités d’information. Le second type ne renvoie pas de données
mais réalise une action sur les données stockées dans la base de données
(comme dans le cas d’une instruction DELETE). Certains serveurs de base de
données gèrent les deux types d’opération dans une même procédure.
Les procédures stockées qui renvoient des données le font de différentes façons,
en fonction de leur composition et de l’utilisation du système de base de
données. Certaines, comme InterBase, renvoient toutes les données (ensembles de
données et unités d’information) uniquement avec des paramètres de sortie.
D’autres peuvent renvoyer un curseur vers les données. D’autres encore, comme
Microsoft SQL Server et Sybase, peuvent renvoyer un ensemble de données et
des unités.
Dans les applications Delphi, l’accès aux procédures stockées est fourni par les
composants TStoredProc et TQuery. Le choix du composant à utiliser dépend du
codage de la procédure stockée, de la façon dont les données éventuelles sont
renvoyées et de l’utilisation du système de base de données. Les composants
TStoredProc et TQuery sont tous les deux des descendants de TDataSet et héritent
leurs comportements de TDataSet. Pour plus d’informations sur TDataSet, voir
chapitre 18, “Présentation des ensembles de données”.

Manipulation des procédures stockées 22-1


Quand utiliser les procédures stockées ?

Un composant procédure stockée permet d’exécuter des procédures stockées qui


ne renvoient aucune donnée pour extraire des unités d’information sous la forme
de paramètres de sortie et transmettre un ensemble de données renvoyé à un
composant source de données associé (ce dernier étant propre à la base de
données). Le composant procédure stockée permet aux valeurs d’être transmises
à la procédure stockée, et d’en être renvoyées, par le biais de paramètres, dont
chacun est défini dans la propriété Params. Le composant procédure stockée offre
également une méthode GetResults qui oblige la procédure stockée à renvoyer un
ensemble de données (certains serveurs de base de données impose cela pour la
génération d’un ensemble résultat). Le composant procédure stockée convient
parfaitement pour utiliser des procédures stockées qui ne renvoient aucune
donnée ou n’en renvoient que par le biais de paramètres de sortie.
Un composant requête est essentiellement utilisé pour exécuter des procédures
stockées qui renvoient des ensembles de données. C’est par exemple le cas des
procédures stockées InterBase qui ne renvoient que des ensembles de données
par le biais de paramètres de sortie. Le composant requête permet aussi
d’exécuter une procédure stockée qui ne renvoie pas d’ensemble de données ou
de valeurs de paramètre de sortie.
Utilisez les paramètres pour transmettre différentes valeurs à une procédure
stockée ou pour en renvoyer à partir de celle-ci. Les valeurs de paramètre
d’entrée sont par exemple utilisées dans la clause WHERE d’une instruction
SELECT dans une procédure stockée. Un paramètre de sortie permet à une
procédure stockée de transmettre une valeur unique à l’application appelante.
Certaines procédures stockées renvoient un paramètre résultat. Reportez-vous à
la documentation du serveur de bases de données que vous utilisez pour plus de
détails sur les types de paramètres gérés et sur leur utilisation dans le langage
procédural du serveur.

Quand utiliser les procédures stockées ?


Si des procédures stockées ont été définies sur votre serveur, nous vous
recommandons de les utiliser si elles s’appliquent aux besoins de votre
application. Les procédures stockées sont souvent créées pour gérer des tâches
devant se répéter fréquemment sur le serveur. En règle générale, elles sont utiles
pour les opérations effectuées sur un grand nombre de lignes dans des tables ou
faisant appel à des fonctions statistiques ou mathématiques. Lorsque des
procédures stockées existent sur le serveur de base de données distant, il est
conseillé d’en tirer parti dans vos applications. Il est fort probable qu’elles
contiennent des fonctionnalités qui vous seront utiles et vous pourrez améliorer
les performances de votre application de base de données, car :
• vous bénéficierez de la puissance de traitement et de la vitesse généralement
supérieures du serveur ;
• vous réduirez le volume du trafic réseau, puisque tout le traitement se fera
sur le serveur où se trouvent les données.

22-2 Guide du développeur


Utilisation de procédures stockées

Prenez par exemple le cas d’une application devant calculer une valeur unique,
comme l’écart-type entre des valeurs pour un grand nombre d’enregistrements.
Pour effectuer ce calcul dans votre application, toutes les valeurs utilisées
doivent être extraites du serveur, d’où un accroissement du trafic réseau. Votre
application doit ensuite effectuer les calculs. Comme ce qui vous intéresse, c’est
le résultat final, c’est-à-dire une valeur représentant l’écart-type ; il sera beaucoup
plus efficace qu’une procédure stockée lise les données sur le serveur, effectue
les calculs puis transmette la valeur recherchée à votre application.
Pour plus de détails sur le support des procédures stockées, reportez-vous à la
documentation de votre serveur de base de données.

Utilisation de procédures stockées


L’utilisation d’une procédure stockée dans une application Delphi dépend de son
codage, de la façon dont elle renvoie éventuellement des données, du serveur de
base de données utilisé ou d’une combinaison de ces facteurs.
En général, pour que votre application puisse accéder à une procédure stockée,
vous devez accomplir les étapes suivantes :
1 Instanciez un composant TStoredProc et associez-le éventuellement à une
procédure stockée sur le serveur. Ou instanciez un composant TQuery et
définissez sa propriété SQL pour effectuer une requête SELECT sur la
procédure stockée ou une commande EXECUTE, suivant que la procédure
stockée retourne ou non un ensemble résultat. Pour plus d’informations sur la
création d’une procédure stockée TStoredProc, voir “Création d’un composant
procédure stockée” à la page 22-4. Pour plus d’informations sur la création
d’un composant TQuery, reportez-vous au chapitre 21, “Manipulation des
requêtes”.
2 Si nécessaire, fournissez au composant procédure stockée des paramètres
d’entrée. Si un composant procédure stockée n’est pas associé à une procédure
stockée sur le serveur, vous devez fournir des informations supplémentaires
sur les paramètres d’entrée, comme les noms des paramètres et leur type de
données. Pour plus d’informations sur la transmission de paramètres d’entrée,
voir “Définition des informations sur les paramètres à la conception” à la
page 22-15.
3 Exécutez la procédure stockée.
4 Traitez les paramètres résultat et les paramètres de sortie. Comme pour tout
autre composant ensemble de données, vous pouvez aussi examiner
l’ensemble de données résultat renvoyé par le serveur. Pour plus
d’informations sur les paramètres résultat et les paramètres de sortie, voir
“Utilisation des paramètres de sortie” à la page 22-13 et “Utilisation du
paramètre résultat” à la page 22-14. Pour plus d’informations sur la
visualisation d’enregistrements dans un ensemble de données, voir “Utilisation
de procédures stockées qui renvoient des ensembles de résultats” à la
page 22-6.

Manipulation des procédures stockées 22-3


Utilisation de procédures stockées

Création d’un composant procédure stockée


Pour créer un composant procédure stockée pour un serveur de base de
données :
1 Depuis la page AccèsBD de la palette des composants, placez un composant
procédure stockée dans un module de données.
2 Donnez à la propriété DatabaseName du composant procédure stockée le nom
de la base de données dans laquelle cette procédure est définie. DatabaseName
doit obligatoirement être un alias BDE ou de même valeur que la propriété
DatabaseName d’un TDatabase pouvant se connecter au serveur.
Vous devez en principe spécifier la propriété DatabaseName. Toutefois, si le
serveur de bases de données sur lequel votre application s’exécute est
temporairement indisponible, vous pouvez créer et configurer un composant
procédure stockée en omettant la propriété DatabaseName et en fournissant un
nom de procédure stockée et des paramètres d’entrée, de sortie et résultat à la
conception. Pour plus d’informations sur les paramètres d’entrée, voir
“Utilisation des paramètres d’entrée” à la page 22-12. Pour plus d’informations
sur les paramètres de sortie, voir “Utilisation des paramètres de sortie” à la
page 22-13. Pour plus d’informations sur le paramètre résultat, voir
“Utilisation du paramètre résultat” à la page 22-14.
3 Donnez éventuellement à la propriété StoredProcName le nom de la procédure
stockée à utiliser. Si une valeur a été fournie à la propriété DatabaseName, vous
pouvez sélectionner le nom de la procédure stockée dans la liste déroulante
de la propriété. Un unique composant TStoredProc permet d’exécuter un
nombre quelconque de procédures stockées en affectant à la propriété
StoredProcName un nom valide dans l’application. Il n’est pas
systématiquement souhaitable de définir StoredProcName à la conception.
4 Double-cliquez sur la zone valeur de la propriété Params pour ouvrir l’éditeur
de paramètres de procédure stockée et examiner les paramètres d’entrée et de
sortie relatifs à la procédure. Si vous n’avez pas spécifié de nom de procédure
stockée à l’étape 3, ou si le nom spécifié n’existe pas sur le serveur indiqué
dans la propriété DatabaseName à l’étape 2, aucun paramètre n’apparaît dans
l’éditeur de paramètres de procédure stockée.
Certains serveurs ne renvoient pas de paramètres ou d’informations sur les
paramètres. Pour déterminer le type d’information renvoyé aux applications
client sur les procédures stockées, voir la documentation de votre serveur.
Remarque Si la propriété DatabaseName n’a pas été spécifiée à l’étape 2, vous devez utiliser
l’éditeur de paramètres de procédure stockée pour configurer les paramètres lors
de la phase de conception. Pour plus d’informations sur la définition de
paramètres lors de la conception, voir “Définition des informations sur les
paramètres à la conception” à la page 22-15.

22-4 Guide du développeur


Utilisation de procédures stockées

Création d’une procédure stockée


En règle générale, les procédures stockées sont créées lorsque l’application et ses
bases de données le sont, à l’aide d’outils fournis par le concepteur du système
de base de données. Toutefois, il est possible de créer des procédures stockées à
l’exécution. L’instruction SQL spécifique utilisée dépend du système de base de
données car le langage procédural varie énormément. Consultez la documentation
de votre système de base de données pour connaître le langage procédural géré.
Une procédure stockée peut être créée par une application à l’exécution à l’aide
d’une instruction SQL émise à partir d’un composant TQuery, typiquement avec
une instruction CREATE PROCEDURE. Si des paramètres sont utilisés dans la
procédure stockée, affectez la valeur False à la propriété ParamCheck du composant
TQuery. Ainsi, le composant TQuery ne peut pas confondre le paramètre contenu
dans la nouvelle procédure stockée avec l’un de ses propres paramètres.
Remarque Vous pouvez aussi utiliser l’explorateur SQL pour examiner, modifier et créer
des procédures stockées sur le serveur.
Une fois que la propriété SQL possède l’instruction permettant de créer la
procédure stockée, exécutez-la en appelant la méthode ExecSQL.
with Query1 do begin
ParamCheck := False;
with SQL do begin
Clear;
Add(‘CREATE PROCEDURE GET_MAX_EMP_NAME’);
Add(‘RETURNS (Max_Name CHAR(15))’);
Add(‘AS’);
Add(‘BEGIN’);
Add(‘ SELECT MAX(LAST_NAME)’);
Add(‘ FROM EMPLOYEE’);
Add(‘ INTO :Max_Name;’);
Add(‘ SUSPEND;’);
Add(‘END’);
end;
ExecSQL;
end;

Préparation et exécution d’une procédure stockée


Pour utiliser une procédure stockée, vous devez d’abord la préparer et l’exécuter.
Vous pouvez le faire :
• au moment de la conception en choisissant OK dans l’éditeur de paramètres ;
• au moment de l’exécution en faisant appel à la méthode Prepare du composant
procédure stockée.
Par exemple, la ligne de code ci-dessous prépare une procédure stockée en vue
de son exécution :
StoredProc1.Prepare;

Manipulation des procédures stockées 22-5


Utilisation de procédures stockées

Remarque Si votre application change les informations relatives aux paramètres lors de
l’exécution (comme lorsque des procédures surchargées Oracle sont utilisées),
vous devez la préparer à nouveau.
Pour exécuter une procédure stockée préparée, faites appel à la méthode
ExecProc du composant procédure stockée. Le code suivant prépare et exécute
une procédure stockée :
StoredProc1.Params[0].AsString := Edit1.Text;
StoredProc1.Prepare;
StoredProc1.ExecProc;
Remarque Si vous tentez d’exécuter une procédure stockée avant qu’elle n’ait été préparée,
le composant procédure stockée le fait automatiquement à votre place, puis
annule sa préparation après son exécution. Si vous envisagez d’exécuter
plusieurs fois une procédure stockée, il est plus efficace d’appeler Prepare vous-
même, puis d’appeler UnPrepare une seule fois, lorsque vous n’avez plus besoin
d’exécuter la procédure.
Lorsque vous exécutez une procédure stockée, elle peut renvoyer une partie ou
l’ensemble des éléments suivants :
• Un ensemble de données composé d’un ou de plusieurs enregistrements
pouvant être visualisés dans des contrôles orientés données associés à la
procédure stockée par l’intermédiaire d’un composant source de données.
• Des paramètres de sortie.
• Un paramètre résultat contenant des informations d’état sur l’exécution de la
procédure stockée.
Pour déterminer les éléments susceptibles d’être renvoyés par une procédure
stockée sur votre serveur, voir la documentation du serveur.

Utilisation de procédures stockées qui renvoient des ensembles de


résultats
Les procédures stockées qui renvoient des données dans des ensembles de
données, sous la forme de lignes et de colonnes de données, doivent la plupart
du temps être utilisées avec un composant requête. Toutefois, si le serveur de
base de données permet aux procédures stockées de renvoyer les ensembles de
données, les procédures stockées peuvent s’affranchir du composant requête.

Extraction d’un ensemble de résultat avec un composant TQuery


Pour extraire un ensemble de données d’une procédure stockée avec un
composant TQuery :
1 Instanciez un composant requête.
2 Dans la propriété TQuery.SQL, écrivez une requête SELECT qui utilise le nom
de la procédure stockée à la place d’un nom de table.

22-6 Guide du développeur


Utilisation de procédures stockées

3 Si la procédure stockée requiert des paramètres d’entrée, exprimez leurs


valeurs après le nom de la procédure, entre parenthèses, en les séparant par
des virgules.
4 Attribuez la valeur True à la propriété Active ou appelez la méthode Open.
Par exemple, la procédure stockée InterBase GET_EMP_PROJ suivante accepte
une valeur utilisant le paramètre d’entrée EMP_NO et renvoie un ensemble de
données par le biais du paramètre de sortie PROJ_ID.
CREATE PROCEDURE GET_EMP_PROJ (EMP_NO SMALLINT)
RETURNS (PROJ_ID CHAR(5))
AS
BEGIN
FOR SELECT PROJ_ID
FROM EMPLOYEE_PROJECT
WHERE EMP_NO = :EMP_NO
INTO :PROJ_ID
DO
SUSPEND;
END
L’instruction SQL émise à partir d’un composant TQuery pour utiliser cette
procédure stockée serait :
SELECT *
FROM GET_EMP_PROJ(52)

Extraction d’un ensemble de résultat avec un composant TStoredProc


Pour extraire un ensemble de données d’une procédure stockée avec un
composant TStoredProc :
1 Instanciez un composant procédure stockée.
2 Dans la propriété StoredProcName, spécifiez le nom de la procédure stockée.
3 Si la procédure stockée requiert des paramètres d’entrée, fournissez leurs
valeurs à l’aide de la propriété Params ou de la méthode ParamByName.
4 Attribuez la valeur True à la propriété Active ou appelez la méthode Open.
Par exemple, la procédure stockée Sybase GET_EMPLOYEES suivante accepte un
paramètre d’entrée appelé @EMP_NO et renvoie un ensemble de résultat en
fonction de cette valeur.
CREATE PROCEDURE GET_EMPLOYEES @EMP_NO SMALLINT
AS SELECT EMP_NAME, EMPLOYEE_NO FROM EMPLOYEE_TABLE
WHERE (EMPLOYEE_NO = @EMP_NO)
Le code Delphi qui permet de fournir une valeur au paramètre et d’activer le
composant procédure stockée est le suivant :
with StoredProc1 do begin
Close;
ParamByName(‘EMP_NO’).AsSmallInt := 52;
Active := True;
end;

Manipulation des procédures stockées 22-7


Utilisation de procédures stockées

Utilisation de procédures stockées qui renvoient des données à


l’aide de paramètres
Les procédures stockées peuvent être conçues pour extraire des unités
d’information, et non pas des lignes entières de données, par le biais de
paramètres. Par exemple, une procédure stockée peut extraire la valeur maximale
d’une colonne, lui ajouter la valeur 1 et renvoyer la valeur obtenue à
l’application. De telles procédures stockées peuvent être utilisées et les valeurs
inspectées à l’aide d’un composant TQuery ou TStoredProc. La meilleure méthode
d’extraction de valeurs de paramètre est celle utilisant un composant TStoredProc.

Extraction de valeurs individuelles avec un composant TQuery


Les valeurs de paramètre extraites via un composant TQuery prennent la forme
d’un ensemble de données à ligne unique, même si un seul paramètre est
renvoyé par la procédure stockée. Pour extraire des valeurs individuelles à partir
de paramètres de procédure stockée à l’aide d’un composant TQuery :
1 Instanciez un composant requête.
2 Dans la propriété TQuery.SQL, écrivez une requête SELECT qui utilise le nom
de la procédure stockée au lieu du nom d’une table. La clause SELECT de
cette requête peut spécifier le paramètre par son nom, comme s’il s’agissait
d’une colonne d’une table, ou simplement utiliser l’opérateur * pour extraire
toutes les valeurs de paramètre.
3 Si la procédure stockée requiert des paramètres d’entrée, exprimez leurs
valeurs après le nom de la procédure, entre parenthèses, en les séparant par
des virgules.
4 Attribuez la valeur Active à la propriété True ou appelez la méthode Open.
Par exemple, la procédure stockée InterBase GET_HIGH_EMP_NAME suivante
extrait la dernière valeur alphabétique de la colonne LAST_NAME d’une table
nommée EMPLOYEE. La procédure stockée renvoie cette valeur dans le
paramètre de sortie High_Last_Name.
CREATE PROCEDURE GET_HIGH_EMP_NAME
RETURNS (High_Last_Name CHAR(15))
AS
BEGIN
SELECT MAX(LAST_NAME)
FROM EMPLOYEE
INTO :High_Last_Name;
SUSPEND;
END
L’instruction SQL émise à partir d’un composant TQuery pour utiliser cette
procédure stockée serait :
SELECT High_Last_Name
FROM GET_HIGH_EMP_NAME

22-8 Guide du développeur


Utilisation de procédures stockées

Extraction de valeurs individuelles avec un composant TStoredProc


Pour extraire des valeurs individuelles de paramètres de sortie de procédure
stockée avec un composant TStoredProc :
1 Instanciez un composant procédure stockée.
2 Dans la propriété StoredProcName, spécifiez le nom de la procédure stockée.
3 Si la procédure stockée requiert des paramètres d’entrée, fournissez leurs
valeurs à l’aide de la propriété Params ou de la méthode ParamByName.
4 Appelez la méthode ExecProc.
5 Inspectez les valeurs de chaque paramètre de sortie avec la propriété Params
ou la méthode ParamByName.
Par exemple, la procédure stockée InterBase GET_HIGH_EMP_NAME suivante
extrait la dernière valeur alphabétique de la colonne LAST_NAME d’une table
nommée EMPLOYEE. La procédure stockée renvoie cette valeur dans le
paramètre de sortie High_Last_Name.
CREATE PROCEDURE GET_HIGH_EMP_NAME
RETURNS (High_Last_Name CHAR(15))
AS
BEGIN
SELECT MAX(LAST_NAME)
FROM EMPLOYEE
INTO :High_Last_Name;
SUSPEND;
END
Le code Delphi permettant d’obtenir la valeur du paramètre de sortie
High_Last_Name et de la stocker dans la propriété Text d’un composant TEdit
est :
with StoredProc1 do begin
StoredProcName := ‘GET_HIGH_EMP_NAME’;
ExecProc;
Edit1.Text := ParamByName(‘High_Last_Name’).AsString;
end;

Utilisation de procédures stockées pour manipuler les données


Les procédures stockées peuvent être codées pour qu’elles ne renvoient aucune
donnée et ne réalisent que certaines tâches dans la base de données. Les
opérations SQL impliquant les instructions INSERT et DELETE illustrent ce type
de procédure stockée. Par exemple, au lieu de permettre à un utilisateur de
supprimer une ligne directement, une procédure stockée peut être utilisée à cet
effet. Cela permet à la procédure stockée de contrôler ce qui est supprimé et
aussi de gérer tous les aspects d’intégrité référentielle, comme la suppression en
cascade de lignes dans des tables dépendantes.

Manipulation des procédures stockées 22-9


Utilisation de procédures stockées

Exécution d’une procédure stockée d’action avec un composant TQuery


Pour exécuter une procédure stockée d’action avec un composant TQuery :
1 Instanciez un composant requête.
2 Dans la propriété TQuery.SQL, incluez la commande nécessaire à l’exécution
de la procédure stockée et le nom de la procédure stockée. La commande
permettant d’exécuter une procédure stockée peut varier d’un système de base
de données à l’autre. Dans InterBase, il s’agit de la commande EXECUTE
PROCEDURE.
3 Si la procédure stockée requiert des paramètres d’entrée, exprimez leurs
valeurs après le nom de la procédure, entre parenthèses, en les séparant par
des virgules.
4 Appelez la méthode TQuery.ExecSQL.
Par exemple, la procédure stockée InterBase ADD_EMP_PROJ suivante ajoute
une nouvelle ligne à la table EMPLOYEE_PROJECT. Aucun ensemble de données
n’est renvoyé et aucune valeur n’est renvoyée dans un paramètre de sortie.
CREATE PROCEDURE ADD_EMP_PROJ (EMP_NO SMALLINT, PROJ_ID CHAR(5))
AS
BEGIN
BEGIN
INSERT INTO EMPLOYEE_PROJECT (EMP_NO, PROJ_ID)
VALUES (:EMP_NO, :PROJ_ID);
WHEN SQLCODE -530 DO
EXCEPTION UNKNOWN_EMP_ID;
END
SUSPEND;
END
L’instruction SQL émise à partir d’un composant TQuery pour exécuter cette
procédure stockée serait :
EXECUTE PROCEDURE ADD_EMP_PROJ(20, “GUIDE”)

Exécution d’une procédure stockée d’action avec un composant


TStoredProc
Pour extraire des valeurs individuelles de paramètres de sortie de procédure
stockée avec un composant TStoredProc :
1 Instanciez un composant procédure stockée.
2 Dans la propriété StoredProcName, spécifiez le nom de la procédure stockée.
3 Si la procédure stockée requiert des paramètres d’entrée, fournissez leurs
valeurs à l’aide de la propriété Params ou de la méthode ParamByName.
4 Appelez la méthode ExecProc.

22-10 Guide du développeur


Présentation des paramètres des procédures stockées

Par exemple, la procédure stockée InterBase ADD_EMP_PROJ suivante ajoute


une nouvelle ligne à la table EMPLOYEE_PROJECT. Aucun ensemble de données
n’est renvoyé et aucune valeur n’est renvoyée dans un paramètre de sortie.
CREATE PROCEDURE ADD_EMP_PROJ (EMP_NO SMALLINT, PROJ_ID CHAR(5))
AS
BEGIN
BEGIN
INSERT INTO EMPLOYEE_PROJECT (EMP_NO, PROJ_ID)
VALUES (:EMP_NO, :PROJ_ID);
WHEN SQLCODE -530 DO
EXCEPTION UNKNOWN_EMP_ID;
END
SUSPEND;
END
Le code Delphi permettant d’exécuter la procédure stockée ADD_EMP_PROJ est :
with StoredProc1 do begin
StoredProcName := ‘ADD_EMP_PROJ’;
ExecProc;
end;

Présentation des paramètres des procédures stockées


Quatre types de paramètres peuvent être associés aux procédures stockées :
• les paramètres d’entrée : utilisés pour transmettre à la procédure stockée des
valeurs à traiter ,
• les paramètres de sortie : utilisés par une procédure stockée pour transmettre à
une application des valeurs de retour ;
• les paramètres d’entrée/sortie : utilisés pour transmettre à une procédure stockée
des valeurs à traiter et aussi utilisés par la procédure stockée pour transmettre
à une application des valeurs de retour ;
• un paramètre résultat : utilisé par des procédures stockées pour renvoyer une
erreur ou une valeur d’état à une application. Une procédure stockée ne peut
renvoyer qu’un seul paramètre résultat.
Le type de paramètre utilisé par la procédure stockée dépend à la fois de
l’implémentation langage des procédures stockées sur votre serveur de bases de
données et de l’instance de chaque procédure. Par exemple, certaines procédures
stockées individuelles ne sont implémentées sur un serveur que si elles utilisent
des paramètres d’entrée. Par ailleurs, d’autres utilisations de paramètres sont
propres à chaque serveur. Par exemple, les procédures stockées sur les serveurs
MS-SQL Server et Sybase renvoient toujours un paramètre résultat, mais
l’implémentation InterBase d’une procédure stockée ne renvoie jamais de
paramètre résultat.

Manipulation des procédures stockées 22-11


Présentation des paramètres des procédures stockées

L’accès aux paramètres de procédure stockée est fourni par les objets TParam de
la propriété TStoredProc.Params. Si le nom de la procédure stockée est spécifié à
la conception dans la propriété StoredProcName, un objet TParam est
automatiquement créé pour chaque paramètre et ajouté à la propriété Params. Si
le nom de la procédure stockée n’est spécifié qu’à l’exécution, les objets TParam
doivent être créés par programmation à ce moment là. La non spécification de la
procédure stockée et la création manuelle des objets TParam permet l’utilisation
d’un seul composant TStoredProc avec un nombre quelconque de procédures
stockées disponibles.
Remarque Certaines procédures stockées renvoient un ensemble de données en plus des
paramètres de sortie et du paramètre résultat. Les applications peuvent afficher
des enregistrements d’ensemble de données dans des contrôles orientés données,
mais doivent traiter séparément les paramètres résultat et de sortie. Pour plus
d’informations sur l’affichage des enregistrements dans des contrôles orientés
données, voir “Utilisation de procédures stockées qui renvoient des ensembles de
résultats” à la page 22-6.

Utilisation des paramètres d’entrée


Les applications utilisent les paramètres d’entrée pour transmettre des valeurs de
donnée uniques à une procédure stockée. Ces valeurs sont ensuite utilisées dans
les instructions SQL à l’intérieur de la procédure stockée, comme dans le cas
d’une valeur de comparaison pour une clause WHERE. Si une procédure stockée
requiert un paramètre d’entrée, affectez une valeur au paramètre avant
d’exécuter la procédure stockée.
Si une procédure stockée renvoie un ensemble de données et est utilisée par le
biais d’une requête SELECT dans un composant TQuery, fournissez les valeurs
de paramètre d’entrée après le nom de la procédure, entre parenthèses, en les
séparant par des virgules. Par exemple, l’instruction SQL suivante extrait les
données d’une procédure stockée appelée GET_EMP_PROJ et fournit une valeur
de paramètre d’entrée de 52.
SELECT PROJ_ID
FROM GET_EMP_PROJ(52
Si une procédure stockée est exécutée avec un composant TStoredProc, utilisez la
propriété Params ou l’accès à la méthode ParamByName pour définir chaque
paramètre d’entrée. Utilisez la propriété TParam appropriée pour le type de
données du paramètre, comme la propriété TParam.AsString pour un paramètre
de type CHAR. Définissez les valeurs de paramètre d’entrée avant d’exécuter ou
d’activer le composant TStoredProc. Dans l’exemple suivant, la valeur 52 est
attribuée au paramètre EMP_NO (de type SMALLINT) de la procédure stockée
GET_EMP_PROJ.
with StoredProc1 do begin
ParamByName(‘EMP_NO’).AsSmallInt := 52;
ExecProc;
end;

22-12 Guide du développeur


Présentation des paramètres des procédures stockées

Utilisation des paramètres de sortie


Une procédure stockée utilise les paramètres de sortie pour transmettre des
valeurs de donnée uniques à une application qui l’appelle. Les paramètres de
sortie sont des valeurs uniquement attribuées par la procédure stockée lorsque
celle-ci a été exécutée. Inspectez les paramètres de sortie à partir d’une
application pour extraire leurs valeurs après l’appel de la méthode
TStoredProc.ExecProc.
Utilisez la propriété TStoredProc.Params ou la méthode TStoredProc.ParamByName
pour référencer l’objet TParam qui représente un paramètre et inspecter sa valeur.
Par exemple, le code suivant permet d’extraire la valeur d’un paramètre et de la
stocker dans la propriété Text d’un composant TEdit :
with StoredProc1 do begin
ExecProc;
Edit1.Text := Params[0].AsString;
end;
La plupart des procédures stockées renvoient un ou plusieurs paramètres de
sortie. Les valeurs renvoyées peuvent être les valeurs uniques d’une procédure
stockée ne renvoyant pas d’ensemble de données, un ensemble de valeurs
renvoyé par une procédure renvoyant aussi un ensemble de données ou bien des
valeurs n’ayant pas de correspondance directe avec un enregistrement individuel
de l’ensemble de données renvoyé par la procédure stockée. En ce qui concerne
les paramètres de sortie, l’implémentation des procédures stockées diffère pour
chaque serveur.
Remarque Le code source d’une procédure stockée Informix peut indiquer qu’elle renvoie
des paramètres de sortie même si aucune information relative aux paramètres de
sortie n’est visible dans l’éditeur de paramètres de procédure stockée. Informix
convertit les paramètres de sortie en un ensemble de données mono-
enregistrement pouvant être visualisé dans les contrôles orientés données de
votre application.

Utilisation des paramètres d’entrée / sortie


Les paramètres d’entrée/sortie cumulent les fonctions remplies séparément par
les paramètres d’entrée et les paramètres de sortie. Une application utilise un
paramètre d’entrée/sortie pour transmettre une valeur de donnée unique à une
procédure stockée, qui à son tour réutilise le paramètre d’entrée/sortie pour
transmettre une valeur de donnée unique à l’application appelante. Comme dans
le cas des paramètres d’entrée, la valeur d’entrée d’un paramètre d’entrée/sortie
doit être définie avant que le composant requête ou procédure stockée impliqué
ne soit activé. De même, la valeur de sortie d’un paramètre d’entrée/sortie n’est
disponible qu’après exécution de la procédure stockée.

Manipulation des procédures stockées 22-13


Présentation des paramètres des procédures stockées

Dans l’exemple de procédure stockée Oracle suivant, le paramètre IN_OUTVAR


est un paramètre d’entrée/sortie.
CREATE OR REPLACE PROCEDURE UPDATE_THE_TABLE (IN_OUTVAR IN OUT INTEGER)
AS
BEGIN
UPDATE ALLTYPETABLE
SET NUMBER82FLD = IN_OUTVAR
WHERE KEYFIELD = 0;
IN_OUTVAR:=1;
END UPDATE_THE_TABLE;
Dans le code Delphi suivant, une valeur d’entrée est affectée à IN_OUTVAR, la
procédure stockée est exécutée et la valeur de sortie contenue dans IN_OUTVAR
est inspectée et stockée dans une mémoire de variable.
with StoredProc1 do begin
ParamByName(‘IN_OUTVAR’).AsInteger := 103;
ExecProc;
IntegerVar := ParamByName(‘IN_OUTVAR’).AsInteger;
end;

Utilisation du paramètre résultat


En plus de renvoyer des paramètres de sortie et un ensemble de données,
certaines procédures stockées renvoient aussi un paramètre résultat unique. Le
paramètre résultat est généralement utilisé pour indiquer une erreur ou le
nombre d’enregistrements traités suite à l’exécution de la procédure stockée.

Pour savoir si votre serveur de bases de données supporte les paramètres


résultat, voir la documentation du serveur. Les paramètres résultat sont des
valeurs uniquement attribuées par la procédure stockée lorsque celle-ci a été
exécutée. Inspectez un paramètre résultat à partir d’une application pour extraire
sa valeur après l’appel de la méthode TStoredProc.ExecProc.
Utilisez la propriété TStoredProc.Params ou la méthode TStoredProc.ParamByName
pour référencer l’objet TParam qui représente le paramètre résultat et inspecter sa
valeur.
DateVar := StoredProc1.ParamByName(‘MyOutputParam’).AsDate;

Accès aux paramètres en mode conception


Si vous vous connectez à un serveur de base de données distant en définissant
les propriétés DatabaseName et StoredProcName en mode conception, vous pouvez
utiliser l’éditeur de paramètres de procédure stockée pour visualiser le nom et le
type de données de chaque paramètre d’entrée. Vous pouvez aussi définir les
valeurs des paramètres d’entrée qui seront transmis au serveur lors de
l’exécution de la procédure stockée.

22-14 Guide du développeur


Présentation des paramètres des procédures stockées

Important Si vous modifiez les noms et les types de données des paramètres d’entrée
rapportés par le serveur, une exception est déclenchée à l’exécution de la
procédure stockée.
Certains serveurs comme Informix ne rapportent pas les noms de paramètres ou
leur type de données. Dans ce cas, utilisez l’explorateur SQL ou les utilitaires du
constructeur pour consulter le code source de la procédure stockée sur le serveur
afin de déterminer les paramètres d’entrée et leur type de données. Voir l’aide
en ligne de l’explorateur SQL pour de plus amples informations.
Si, au moment de la conception, vous ne disposez pas de la liste de paramètres
d’une procédure stockée sur un serveur distant (parce que vous n’êtes pas
connecté à un serveur, par exemple), vous devez appeler l’éditeur de paramètres
de procédure stockée, puis définir la liste des paramètres requis et leur affecter
un type de données et une valeur. Pour plus d’informations sur l’utilisation de
l’éditeur de paramètres de procédure stockée, voir “Définition des informations
sur les paramètres à la conception” à la page 22-15.

Définition des informations sur les paramètres à la conception


Lors de la conception, vous pouvez appeler l’éditeur de paramètres de procédure
stockée pour définir les paramètres et leurs valeurs.
L’éditeur de collection de paramètres vous permet de définir les paramètres de
procédure stockée. Si vous définissez les propriétés DatabaseName et
StoredProcName du composant TStoredProc lors de la conception, tous les
paramètres existants apparaissent dans l’éditeur de collection. Si vous ne
définissez pas ces deux propriétés, aucun paramètre n’apparaît et vous devez les
ajouter manuellement. De plus, certains types de bases de données ne renvoient
pas toutes les informations sur les paramètres, telles que leurs types. Pour ces
systèmes de base de données, utilisez l’explorateur SQL pour inspecter les
procédures stockées, déterminer les types puis configurer les paramètres par le
biais de l’éditeur de collection et de l’inspecteur d’objets. Pour définir les
paramètres de procédure stockée en mode conception, procédez comme suit :
1 Définissez éventuellement les propriétés DatabaseName et StoredProcName.
2 Dans l’inspecteur d’objets, appelez l’éditeur de collection de paramètres en
cliquant sur le bouton à points de suspension dans le champ Params.
3 Si les propriétés DatabaseName et StoredProcName ne sont pas définies, aucun
paramètre n’apparaît dans l’éditeur de collection. Ajoutez manuellement les
définitions de paramètres en cliquant avec le bouton droit sur l’éditeur de
collection et en sélectionnant Ajouter dans le menu contextuel.
4 Sélectionnez les paramètres individuellement dans l’éditeur de collection pour
afficher leurs propriétés dans l’inspecteur d’objets.
5 Si aucun type n’est automatiquement spécifié pour la propriété ParamType,
sélectionnez un type de paramètre (Entrée, Sortie, Entrée/sortie ou Résultat) dans
la liste déroulante de la propriété.

Manipulation des procédures stockées 22-15


Présentation des paramètres des procédures stockées

6 Si aucun type de données n’est automatiquement spécifié pour la propriété


DataType, sélectionnez un type de données dans la liste déroulante de la
propriété. Pour renvoyer un ensemble résultat d’une procédure stockée Oracle,
attribuez la valeur Curseur au type de champ.
7 Utilisez la propriété Value pour spécifier éventuellement une valeur initiale
pour un paramètre d’entrée ou d’entrée/sortie.
Si vous cliquez avec le bouton droit sur l’éditeur de collection de paramètres, un
menu contextuel permettant de définir les paramètres apparaît. En fonction des
paramètres listés ou sélectionnés, les options disponibles permettent d’ajouter de
nouveaux paramètres, de supprimer des paramètres existants, de déplacer les
paramètres vers le haut ou le bas de liste et de sélectionner tous les paramètres
listés.
Vous pouvez modifier la définition de n’importe quel objet TParam que vous
ajoutez, mais les attributs des objets TParam que vous ajoutez doivent
correspondre à ceux des paramètres de la procédure stockée sur le serveur. Pour
modifier l’objet TParam d’un paramètre, sélectionnez-le dans l’éditeur de
collection de paramètres et modifiez ses valeurs de propriété dans l’inspecteur
d’objets.
Remarque Sybase, MS-SQL et Informix ne renvoient pas les informations sur les types des
paramètres. Utilisez l’explorateur SQL pour déterminer ces informations.
Remarque Informix ne renvoie pas les informations sur les types de données. Utilisez
l’explorateur SQL pour déterminer ces informations.
Remarque Vous ne pouvez jamais définir les valeurs des paramètres de sortie et résultat.
Les valeurs de ces types de paramètres sont définies par l’exécution de la
procédure stockée.

Création de paramètres en mode exécution


Si le nom d’une procédure stockée n’est pas spécifié dans StoredProcName avant
l’exécution, aucun objet TParam n’est automatiquement créé pour les paramètres
et les objets doivent être créés par programmation. La méthode TParam.Create ou
la méthode TParams.AddParam permet de le faire.
Par exemple, la procédure stockée InterBase GET_EMP_PROJ suivante requiert
un paramètre d’entrée (EMP_NO) et un paramètre de sortie (PROJ_ID).
CREATE PROCEDURE GET_EMP_PROJ (EMP_NO SMALLINT)
RETURNS (PROJ_ID CHAR(5))
AS
BEGIN
FOR SELECT PROJ_ID
FROM EMPLOYEE_PROJECT
WHERE EMP_NO = :EMP_NO
INTO :PROJ_ID
DO
SUSPEND;
END

22-16 Guide du développeur


Présentation des paramètres des procédures stockées

Le code Delphi permettant d’associer cette procédure stockée à un composant


TStoredProc appelé StoredProc1 et de créer des objets TParam pour les deux
paramètres avec la méthode TParam.Create est le suivant :
var
P1, P2: TParam;
begin
...
with StoredProc1 do begin
StoredProcName := ’GET_EMP_PROJ’;
Params.Clear;
P1 := TParam.Create(Params, ptInput);
P2 := TParam.Create(Params, ptOutput);
try
Params[0].Name := ‘EMP_NO’;
Params[1].Name := ‘PROJ_ID’;
ParamByname(‘EMP_NO’).AsSmallInt := 52;
ExecProc;
Edit1.Text := ParamByname(‘PROJ_ID’).AsString;
finally
P1.Free;
P2.Free;
end;
end;
...
end;

Liaison de paramètres
Lorsque vous préparez et exécutez une procédure stockée, ses paramètres
d’entrée sont automatiquement liés aux paramètres du serveur.
La propriété ParamBindMode permet de déterminer la façon dont les paramètres
de votre composant procédure stockée doivent être liés aux paramètres du
serveur. Par défaut la propriété ParamBindMode vaut pbByName, ce qui signifie
que les paramètres du composant procédure stockée sont mis en correspondance
avec ceux du serveur d’après leur nom. C’est la méthode la plus simple pour lier
des paramètres.
Certains serveurs supportent également la liaison de paramètres d’après leur
valeur ordinale, l’ordre dans lequel les paramètres apparaissent dans la
procédure stockée. Dans ce cas, l’ordre dans lequel les paramètres sont spécifiés
dans l’éditeur de collection de paramètres est important. Le premier paramètre
spécifié est mis en correspondance avec le premier paramètre d’entrée sur le
serveur, le deuxième paramètre est mis en correspondance avec le deuxième
paramètre sur le serveur, et ainsi de suite. Si votre serveur supporte la liaison de
paramètres d’après leur valeur ordinale, vous pouvez attribuer la valeur
pbmByNumber à la propriété ParamBindMode.
Astuce Si vous attribuez la valeur pbByNumber à la propriété ParamBindMode, vous devez
spécifier les bons types de paramètres dans un ordre correct. Il est possible de
visualiser le code source de la procédure stockée d’un serveur dans l’explorateur
SQL afin de déterminer si l’ordre et le type des paramètres sont corrects.

Manipulation des procédures stockées 22-17


Visualisation des informations sur les paramètres à la conception

Visualisation des informations sur les paramètres à la conception


Si vous accédez à un serveur de bases de données lors de la conception, deux
méthodes vous permettent de visualiser les informations sur les paramètres
utilisés par une procédure stockée :
• Appelez l’explorateur SQL pour visualiser le code source d’une procédure
stockée sur un serveur distant. Le code source comprend des déclarations de
paramètres qui identifient le type de données et le nom de chaque paramètre.
• Utilisez l’inspecteur d’objets pour visualiser les définitions de propriétés de
chaque objet TParam.
Si vous utilisez des pilotes natifs BDE, vous pouvez utiliser l’explorateur SQL
pour examiner les procédures stockées sur vos serveurs de bases de données. Si
vous utilisez des pilotes ODBC, vous ne pouvez pas examiner les procédures
stockées avec l’explorateur SQL. Bien que l’utilisation de l’explorateur SQL ne
soit pas toujours une option, il peut parfois apporter davantage d’informations
que la visualisation des objets TParam par l’inspecteur d’objets. La quantité
d’informations renvoyées dans l’inspecteur d’objets sur une procédure stockée
dépend de votre serveur de bases de données.
Pour visualiser les définitions de paramètres individuels dans l’inspecteur
d’objets :
1 Sélectionnez le composant procédure stockée.
2 Attribuez à la propriété DatabaseName du composant procédure stockée l’alias
BDE de votre serveur de bases de données (ou la propriété DatabaseName d’un
composant TDatabase).
3 Attribuez à la propriété StoredProcName le nom de la procédure stockée.
4 Cliquez sur le bouton à points de suspension de la propriété
TStoredProc.Params dans l’inspecteur d’objets.
5 Sélectionnez les paramètres individuels dans l’éditeur de collection pour
visualiser la définition de leurs propriétés dans l’inspecteur d’objets.
Pour certains serveurs, il se peut que des informations relatives aux paramètres
soient inaccessibles ou partiellement accessibles.
Dans l’inspecteur d’objets, lors de la visualisation d’objets TParam individuels, la
propriété ParamType indique si le paramètre sélectionné est un paramètre
d’entrée, d’entrée/sortie, de sortie ou de résultat. La propriété DataType indique
le type de données de la valeur du paramètre, tel que chaîne, entier ou date. La
boîte de saisie Valeur vous permet d’entrer une valeur pour le paramètre
d’entrée sélectionné.
Remarque Les serveurs Sybase, MS-SQL et Informix ne renvoient pas d’informations sur les
types de paramètres. Utilisez l’explorateur SQL ou les utilitaires du constructeur
du serveur pour déterminer ces informations.
Remarque Les serveurs Informix ne renvoient pas d’informations sur les types de données.
Utilisez l’explorateur SQL pour déterminer ces informations.

22-18 Guide du développeur


Manipulation des procédures stockées surchargées Oracle

Pour plus d’informations sur la définition des valeurs des paramètres, voir
“Définition des informations sur les paramètres à la conception” à la page 22-15.
Remarque Il n’est pas possible de définir les valeurs des paramètres de sortie et des
paramètres résultat. Les valeurs de ces types de paramètres sont définies par
l’exécution de la procédure stockée.

Manipulation des procédures stockées surchargées Oracle


Les serveurs Oracle permettent la surcharge des procédures stockées, c’est-à-dire
de procédures différentes portant le même nom. La propriété Overload du
composant procédure stockée permet à une application de spécifier la procédure
à exécuter.
Si la valeur d’Overload est zéro (valeur par défaut), il n’y a pas de surcharge. Si
elle vaut 1, le composant procédure stockée exécute la première procédure
stockée portant le nom surchargé ; si elle vaut 2, il exécutera la seconde, et ainsi
de suite.
Remarque Les procédures stockées surchargées peuvent prendre des paramètres d’entrée et
de sortie différents. Reportez-vous à la documentation de votre serveur Oracle
pour plus de détails sur ce point.

Manipulation des procédures stockées 22-19


22-20 Guide du développeur
Chapitre

Utilisation des composants ADO


Chapter 23
23
Les composants ADO encapsulent les fonctionnalités du modèle ADO. ADO
(objets de données ActiveX) est un ensemble d’objets de données qui donnent à
une application la possibilité d’accéder à des données via un fournisseur OLE
DB. Les composants ADO Delphi encapsulent les fonctionnalités de ces objets
ADO et proposent leurs fonctionnalités dans le contexte de composants Delphi.
Les objets ADO les plus importants sont les objets Connection, Command et
Recordset. Ces objets ADO sont directement représentés par les composants
TADOConnection, TADOCommand et les composants d’ensemble de données
ADO. Le modèle ADO propose d’autres objets utilitaires, comme les objets Field
ou Properties, mais généralement ils ne sont pas utilisés directement par un
programmeur Delphi et ne sont pas représentés par des composants dédiés.
L’utilisation d’ADO et des composants ADO permet au programmeur Delphi de
créer des applications de base de données qui ne dépendent pas du moteur de
bases de données Borland (BDE) et qui utilisent à la place ADO pour l’accès aux
données.
Ce chapitre présente chacun des composants ADO et décrit en quoi ils diffèrent
de leurs équivalents BDE. Vous y trouverez des renvois aux rubriques couvrant
des aspects des composants connexion et ensemble de données BDE qui sont
identiques pour leurs équivalents ADO.

Présentation des composants ADO


Outre les composants connexion et ensemble de données utilisant le moteur de
bases de données Borland (BDE), Delphi propose un ensemble de composants
utilisant ADO. Ces composants permettent au programmeur de se connecter à
un stockage de données ADO et d’exécuter des commandes pour obtenir les
données de tables de bases de données.

Utilisation des composants ADO 23-1


Présentation des composants ADO

Ces composants d’accès aux données ADO se connectent à des stockages de


données ADO et agissent sur les données en utilisant uniquement le modèle
ADO. Le BDE n’est pas du tout utilisé dans ce processus. Utilisez les composants
ADO quand ADO est disponible et que vous ne souhaitez pas utiliser le BDE.
ADO version 2.1 (ou supérieure) doit être installé sur l’ordinateur hôte. De plus,
il faut que le logiciel client pour le système de bases de données cible (par
exemple, Microsoft SQL Server) soit installé ainsi qu’un pilote OLE DB ou ODBC
spécifique à ce système de bases de données.
Les composants ADO connexion et ensemble de données sont similaires aux
composants connexion et ensemble de données utilisant le BDE. Le composant
TADOConnection est fonctionnellement analogue au composant TDatabase des
applications BDE. TADOTable est équivalent à TTable, TADOQuery à TQuery et
TADOStoredProc à TStoredProc. Utilisez ces composants ADO de la même
manière et dans les mêmes contextes que leurs équivalents BDE. TADODataSet
n’a pas d’équivalent BDE direct, mais il propose de nombreuses fonctions
similaires à celles existant dans TTable et TQuery. De même, il n’y a pas de
composant BDE comparable à TADOCommand qui joue un rôle particulier dans
l’environnement Delphi/ADO.
Les composants ADO sont représentés par les classes suivantes.

Tableau 23.1
Composant Rôle
TADOConnection Permet d’établir une connexion avec un stockage de données ADO ;
des composants ensemble de données ou commande ADO peuvent
partager cette connexion pour exécuter des commandes, obtenir des
données ou agir sur les métadonnées.
TADODataSet Le composant de base pour obtenir et transformer ses données ; il
peut obtenir les données d’une ou de plusieurs tables; il peut se
connecter directement à un stockage de données ou via un
composant TADOConnection.
TADOTable Permet d’obtenir et de manipuler un ensemble de données provenant
d’une seule table ; il peut se connecter directement à un stockage de
données ou via un composant TADOConnection.
TADOQuery Permet d’obtenir et de manipuler un ensemble de données produit
par une instruction SQL ; il peut également exécuter des instructions
SQL du langage de définition de données (DDL) SQL comme
CREATE TABLE. Il peut se connecter directement à un stockage de
données ou via un composant TADOConnection.
TADOStoredProc Permet d’exécuter des procédures stockées ; il peut exécuter des
procédures stockées renvoyant des données ou exécutant des
instructions DDL. Il peut se connecter directement à un stockage de
données ou via un composant TADOConnection.
TADOCommand Il permet essentiellement d’exécuter des commandes (des instructions
SQL ne produisant pas d’ensemble de résultats); il s’utilise avec un
composant ensemble de données associé. Il peut également obtenir
un ensemble de données d’une table. Il peut se connecter directement
à un stockage de données ou via un composant TADOConnection.

23-2 Guide du développeur


Connexion à des stockages de données ADO

Connexion à des stockages de données ADO


Avant de pouvoir exécuter des commandes ou obtenir des données, une
application doit établir une connexion avec un stockage de données. Même si
chaque composant commande ou ensemble de données ADO d’une application
peut établir sa propre connexion, vous pouvez utiliser un composant
TADOConnection et partager sa connexion entre plusieurs composants ADO.
Quand des composants commande ou ensemble de données ADO sont connectés
à un stockage de données, il peuvent partager la même connexion ou les
composants peuvent établir chacun leur propre connexion. Chaque approche
ayant ses avantages et ses inconvénients.
Cette section décrit comment établir et utiliser une connexion avec un stockage
de données ADO.

Connexion à un stockage de données avec TADOConnection


Des composants commande et ensemble de données ADO peuvent partager une
seule connexion avec un stockage de données. Pour ce faire, l’application doit
contenir un composant TADOConnection pour chaque connexion avec les
stockages de données. Les composants ensemble de données et commande sont
ensuite associés au composant connexion via leur propriété Connection.
En plus de fournir aux composants ensemble de données et commande un
moyen de se connecter à un stockage de données, les composants connexion
proposent des propriétés et méthodes pour activer et désactiver la connexion,
pour accéder directement à l’objet connexion ADO et pour déterminer à tout
moment dans quelle activité est engagé le composant connexion.

Utilisation de TADOConnection ou de la propriété ConnectionString d’un


ensemble de données
Il est possible de connecter directement au stockage de données chaque
commande ou ensemble de données ADO d’une application. Cependant, si vous
utilisez beaucoup de composants commande ou ensemble de données, il est
généralement plus facile de gérer la connexion en utilisant un seul composant
TADOConnection pour établir une connexion partagée par les composants
commande et ensemble de données. Pour davantage d’informations sur la
manière de connecter directement des commandes ou des ensembles de données
à un stockage de données, voir la section “Connexion à un stockage de données
en utilisant des composants ensemble de données ADO” à la page 23-14.
L’utilisation du composant TADOConnection pour établir la connexion offre
davantage de moyens de contrôler la connexion que si vous connectez
individuellement chaque composant commande ou ensemble de données. Ce
contrôle accru est proposé par les propriétés, méthodes et événements de
TADOConnection dont les fonctionnalités ne sont pas accessibles autrement.

Utilisation des composants ADO 23-3


Connexion à des stockages de données ADO

Spécification de la connexion
Si vous voulez utiliser un composant TADOConnection pour fournir une
connexion partagée à des composants ensemble de données et commande ADO,
il faut tout d’abord établir la connexion. Pour ce faire, vous devez spécifier les
informations de connexion appropriées dans la propriété ConnectionString du
composant connexion. A la conception, appelez l’éditeur de chaîne de connexion
en cliquant, dans l’inspecteur d’objet, sur le bouton points de suspension de la
propriété ConnectionString. Cette boîte de dialogue, fournie par le système ADO,
vous permet de construire de manière interactive la chaîne de connexion en
sélectionnant dans des listes les éléments de connexion (comme le fournisseur ou
le serveur). A l’exécution, affectez une valeur String contenant les informations
de connexion à la propriété ConnectionString. L’affectation de la valeur True à la
propriété Connected du composant connexion active la connexion. Cela constitue
un moyen commode de tester la connexion à la conception.
ADOConnection1.ConnectionString := 'Provider=ProviderName;Remote Server=ServerReference';
La propriété ConnectionString contient plusieurs paramètres de connexion séparés
par des points-virgules. Ces paramètres peuvent spécifier le nom d’un
fournisseur, un nom d’utilisateur et un mot de passe (pour l’identification) et
une référence identifiant un serveur distant. La propriété ConnectionString peut
également contenir le nom d’un fichier contenant les paramètres de la connexion.
Un tel fichier doit avoir le même contenu que la propriété ConnectionString : un
ou plusieurs paramètres ayant chacun leur valeur et séparés par des points-
virgules. Pour une liste des paramètres gérés par ADO pour la propriété
ConnectionString, voir l’aide en ligne de la VCL.
Une fois les informations de connexion spécifiées, il est possible d’associer au
composant connexion des composants ensemble de données et commande. Pour
ce faire, affectez une référence au composant connexion dans la propriété
Connection de chaque composant ensemble de données et commande. A la
conception, sélectionnez le composant connexion souhaité dans la liste déroulante
affichée par la propriété Connection dans l’inspecteur d’objet. A l’exécution,
affectez la référence à la propriété Connection. Par exemple, l’instruction suivante
associe un composant TADODataSet à un composant TADOConnection.
ADODataSet1.Connection := ADOConnection1;
Si vous n’activez pas explicitement la connexion en affectant la valeur True à la
propriété Connected du composant connexion, elle est activée automatiquement
quand le premier composant ensemble de données qui l’utilise est activé ou
lorsque la première commande est exécutée pour un composant commande.

Accès à l’objet connexion


Utilisez la propriété ConnectionObject de TADOConnection pour accéder
directement à l’objet connexion ADO sous-jacent. En utilisant cette référence,
vous pouvez accéder aux propriétés et appeler les méthodes de l’objet ADO
Connection sous-jacent depuis une application.

23-4 Guide du développeur


Connexion à des stockages de données ADO

L’utilisation de ConnectionObject pour accéder directement à l’objet Connection


ADO sous-jacent suppose une bonne connaissance du fonctionnement des objets
ADO en général et de l’objet Connection ADO en particulier. Il n’est pas conseillé
d’utiliser directement l’objet Connection à moins d’être familier des opérations
sur les objets connexion. Reportez-vous à l’aide du SDK Microsoft Data Access
pour des informations spécifiques sur l’utilisation des objets Connection ADO.

Activation et désactivation d’une connexion


Pour activer un composant connexion ADO, affectez la valeur True à la propriété
TADOConnection.Active ou appelez la méthode TADOConnection.Open.
ADOConnection1.Active := True;
Pour que la connexion réussisse, il faut que les informations de connexion
spécifiées par la propriété TADOConnection.ConnectionString définissent
correctement une connexion. Pour davantage d’informations sur les informations
de connexion à spécifier, voir “Spécification de la connexion” à la page 23-4.
L’activation d’un composant connexion ADO déclenche les événements
OnWillConnect et OnConnectComplete du composant connexion ADO et exécute
les gestionnaires de ces événements s’ils ont été définis.
Si le composant connexion n’a pas déjà été activé, il est automatiquement activé si
un composant ensemble de données ou commande est activé. Les composants
ensemble de données déclenchent ceci quand ils sont activés ; les composants
commande le font quand une commande est exécutée. Pour des informations sur
l’association de composants ensemble de données à un composant connexion, voir
“Connexion à un stockage de données avec TADOConnection” à la page 23-3.
Pour désactiver un composant connexion ADO, initialisez sa propriété Active à
False ou appelez sa méthode Close.
ADOConnection1.Close;
Il se produit quatre actions quand le composant connexion est désactivé que ce
soit en utilisant la propriété Active ou la méthode Close :
1 L’événement TADOConnection.OnDisconnect est déclenché.
2 Le gestionnaire de l’événement OnDisconnect est exécuté (s’il est défini).
3 Le composant TADOConnection est désactivé.
4 Tous les composants commande ou ensemble de données ADO associés sont
désactivés.

Comment déterminer ce que fait un composant connexion ?


Vous pouvez, à tout moment de l’existence d’un composant TADOConnection,
consulter sa propriété State pour déterminer quelle action est, éventuellement, en
cours dans le composant connexion.
Si la propriété TADOConnection.State a la valeur stClosed de TObjectStates, cela
indique que l’objet connexion est inactif. La propriété TADOConnection.Active
contient la valeur False et aucun composant commande ou ensemble de données
associé n’est actif.

Utilisation des composants ADO 23-5


Connexion à des stockages de données ADO

La valeur stOpen indique que le composant connexion est actif. Une connexion a
bien été établie avec un stockage de données ADO, sa propriété Active a la
valeur True, et un ou plusieurs des composants commande ou ensemble de
données associés peuvent être actifs.
La valeur stConnecting indique que le composant connexion est en train d’essayer
d’établir la connexion avec le stockage de données ADO spécifié par la propriété
TADOConnection.ConnectionString. Il est possible, dans cet état, d’appeler la
méthode Cancel pour abandonner la tentative de connexion.

Optimisation d’une connexion


Si une application utilise un composant TADOConnection pour connecter les
composants ensemble de données et commande à un stockage de données, vous
pouvez contrôler plus finement les conditions et les attributs de la connexion.
Ces aspects sont implémentés en utilisant les propriétés et gestionnaires
d’événements du composant TADOConnection.

Spécification des attributs d’une connexion


Utilisez la propriété TADOConnection.ConnectOptions pour imposer une connexion
asynchrone. Par défaut, ConnectionOptions a la valeur coConnectUnspecified qui
laisse le serveur décider du meilleur type de connexion. Pour imposer
explicitement une connexion asynchrone, initialisez ConnectOptions à
coAsyncConnect.
Pour configurer une connexion asynchrone ou pour déléguer ce choix au
serveur, affectez l’une des constantes TConnectOption à la propriété
ConnectOptions du composant connexion. Puis, activez la connexion en appelant
sa méthode Open, en initialisant sa propriété Connected à True ou en activant
l’un des composants commande ou ensemble de données associés. Les deux
exemples de code suivants active et désactive, respectivement, des connexions
asynchrones dans le composant connexion spécifié.
procedure TForm1.AsyncConnectButtonClick(Sender: TObject);
begin
with ADOConnection1 do begin
Close;
ConnectOptions := coAsyncConnect;
Open;
end;
end;
procedure TForm1.ServerChoiceConnectButtonClick(Sender: TObject);
begin
with ADOConnection1 do begin
Close;
ConnectOptions := coConnectUnspecified;
Open;
end;
end;

23-6 Guide du développeur


Connexion à des stockages de données ADO

Utilisez la propriété TADOConnection.Attributes pour contrôler l’utilisation par le


composant de la conservation de la validation ou de l’annulation. Attributes peut
contenir l’une des constantes xaCommitRetaining et xaAbortRetaining. Cela permet
de contrôler la conservation des validations ou des annulations de manière
mutuellement exclusive en utilisant la même propriété.
Pour tester si l’une ou l’autre de ces caractéristiques est déjà définie, utilisez
l’opérateur in avec l’une de ces constantes. Activez l’une des caractéristiques en
ajoutant la constante à la propriété ou retirez-la en soustrayant la constante. Les
exemples de code suivant active et désactive, respectivement, la conservation des
validations dans le composant connexion spécifié.
procedure TForm1.RetainingCommitsOnButtonClick(Sender: TObject);
begin
with ADOConnection1 do begin
Close;
if not (xaCommitRetaining in Attributes) then
Attributes := (Attributes + [xaCommitRetaining])
Open;
end;
end;
procedure TForm1.RetainingCommitsOffButtonClick(Sender: TObject);
begin
with ADOConnection1 do begin
Close;
if (xaCommitRetaining in Attributes) then
Attributes := (Attributes - [xaCommitRetaining]);
Open;
end;
end;

Contrôle des dépassements de délais


Vous pouvez contrôler la durée s’écoulant avant que les commandes ou les
connexions tentées soient considérées comme ayant échoué et arrêtées avec les
propriétés TADOConnection.ConnectionTimeout et
TADOConnection.CommandTimeout.
ConnectionTimeout spécifie la durée s’écoulant avant le dépassement de délai pour
établir la connexion avec un stockage de données. Si une connexion démarrée
par un appel de la méthode Open n’a pas réussi avant l’écoulement de la durée
spécifiée par ConnectionTimeout, la tentative de connexion est abandonnée.
Affectez à ConnectionTimeout le nombre de secondes après lequel la tentative de
connexion est abandonnée.
with ADOConnection1 do begin
ConnectionTimeout = 10 {seconds};
Open;
end;

Utilisation des composants ADO 23-7


Connexion à des stockages de données ADO

CommandTimeout spécifie la durée s’écoulant avant le dépassement de délai pour


les commandes. Si la commande démarrée par un appel de la méthode Execute
n’est pas achevée avant l’expiration du délai spécifié par CommandTimeout, la
commande est annulée et ADO génère une exception. Affectez à CommandTimeout
le nombre de secondes après lequel la commande est abandonnée.
with ADOConnection1 do begin
CommandTimeout = 10 {seconds};
Execute('DROP TABLE Employee1997', []);
end;

Contrôle de la boîte de dialogue d’identification


Toute tentative d’établir une connexion avec un stockage de données en utilisant
un composant connexion déclenche un événement de sécurité de connexion,
OnLogin. Une manifestation de cet événement est l’affichage d’une boîte de
dialogue d’identification demandant un nom d’utilisateur et un mot de passe. Il
est possible, si nécessaire, de supprimer l’affichage de cette boîte de dialogue et
de spécifier par code le nom d’utilisateur et le mot de passe.
Pour supprimer la boîte de dialogue d’identification par défaut, commencez par
affecter la valeur False à la propriété LoginPrompt du composant connexion. Puis,
avant d’activer le composant connexion, spécifiez toutes les informations
nécessaires à l’identification via la propriété ConnectionString.
with ADOConnection1 do begin
Close;
LoginPrompt := False;
ConnectionString := 'Provider=NameOfYourProvider;Remote Server=NameOfYourServer;' +
'User Name=JaneDoe;Password=SecretWord';
Connected := True;
end;
Il est également possible de transmettre les informations d’identification au
stockage de données comme paramètres de la méthode Open du composant
connexion.
with ADOConnection1 do begin
Close;
LoginPrompt := False;
ConnectionString := 'Provider=NameOfYourProvider;Remote Server=NameOfYourServer';
Open('JaneDoe', 'SecretWord');
end;
Ces deux routines sont fonctionnellement équivalentes. Mais dans la seconde, le
nom d’utilisateur et le mot de passe ne sont pas spécifiés dans la propriété
ConnectionString mais transmis comme paramètres de la méthode Open. Cela
s’avère utile si les spécifications de la connexion (comme le fournisseur et le
serveur) sont les mêmes pour tous les utilisateurs et seules changent les
informations spécifiques à chaque utilisateur. L’application peut, par exemple,
obtenir les informations spécifiques à l’utilisateur via une boîte de dialogue
d’identification personnalisée et les informations sur le fournisseur et le serveur
depuis une source statique, par exemple des entrées de la base de registres
Windows.

23-8 Guide du développeur


Connexion à des stockages de données ADO

Si, après avoir spécifié les informations de connexion à l’aide de code, la


tentative de connexion échoue, une exception de type EOleException est
déclenchée.

Enumération des tables et procédures stockées


Le composant TADOConnection propose des propriétés permettant d’obtenir les
listes de tables et de procédures stockées accessibles via la connexion. Il propose
également des propriétés permettant d’accéder aux composants ensemble de
données et commande associés au composant connexion.

Accès aux ensembles de données d’une connexion


Les propriétés DataSets et DataSetCount de TADOConnection permettent de
référencer séquentiellement chaque composant ensemble de données associé au
composant connexion. Les composants ensemble de données concernés par les
propriétés DataSets et DataSetCount sont TADODataSet, TADOQuery et
TADOStoredProc. Pour accéder aux composants commande d’une connexion,
utilisez les propriétés Commands et CommandCount.
DataSets est un tableau d’indice de base zéro de références à des composants
ensemble de données ADO. Utilisez un indice dans DataSets représentant la
position d’un ensemble de données. Par exemple, utilisez l’indice 3 pour
désigner le quatrième composant ensemble de données de DataSets.
ShowMessage(ADOConnection1.DataSets[3].Name);
Comme DataSets renvoie une référence de TCustomADODataSet, transtypez cette
référence dans un type de classe descendant pour accéder à une propriété ou
appeler une méthode n’existant que dans la classe dérivée. Ainsi,
TCustomADODataSet n’a pas de propriété SQL mais la classe dérivée TADOQuery
en dispose. Donc, pour accéder à la propriété SQL d’un ensemble de données
référencé par la propriété DataSets, transtypez-le en TADOQuery.
with (ADOConnection1.DataSets[10] as TADOQuery do begin
SQL.Clear;
SQL.Add('SELECT * FROM Species');
Open;
end;
La propriété DataSetCount renvoie le nombre total d’ensembles de données
associés à un composant connexion. Vous pouvez utiliser la propriété
DataSetCount dans une boucle avec la propriété DataSets pour parcourir
séquentiellement tous les composants ensemble de données associés à la
connexion.
var
i: Integer
begin
for i := 0 to (ADOConnection4.DataSetCount) do
ADOConnection4.DataSets[i].Open;
end;

Utilisation des composants ADO 23-9


Connexion à des stockages de données ADO

Accès aux commandes d’une connexion


Les propriétés Commands et CommandCount de TADOConnection ressemblent
beaucoup aux propriétés DataSets et DataSetCount. Mais Commands et
CommandCount renvoient des références à tous les composants TADOCommand
associés au composant connexion. Pour accéder à tous les composants ensemble
de données de la connexion, utilisez les propriétés DataSets et DataSetCount.
Commands est un tableau d’indice de base zéro de références à des composants
commande ADO. Utilisez un indice dans Commands représentant la position
d’une commande dans le tableau. Par exemple, utilisez l’indice 1 pour désigner
le deuxième composant commande de Commands.
Memo1.Lines.Text := ADOConnection1.Commands[1].CommandText;
La propriété CommandCount renvoie le nombre total de commandes associées au
composant connexion. Vous pouvez utiliser la propriété CommandCount dans une
boucle avec la propriété Commands pour parcourir séquentiellement tous les
composants commande associés à la connexion.
var
i: Integer
begin
for i := 0 to (ADOConnection1.CommandCount) do
ADOConnection1.Commands[i].Execute;
end;

Enumération des tables disponibles


Pour disposer d’une liste de toutes les tables de la base de données à laquelle on
accède via l’objet connexion, utilisez la méthode GetTableNames. Cette méthode
copie une liste des noms de tables dans un objet liste de chaînes existant.
Utilisez les éléments de cette liste pour définir la valeur de la propriété
TableName d’un composant TADOTable ou un nom de table dans une instruction
SQL exécutée par un composant TADOQuery.
ADOConnection1.GetTableNames(ListBox1.Items, False);
L’exemple suivant parcourt une liste des noms de tables créée en utilisant la
méthode GetTableNames. Pour chaque table, la routine crée une entrée dans une
autre table avec le nom de la table et le nombre d’enregistrements.
procedure TForm1.Button1Click(Sender: TObject);
var
SL: TStrings;
index: Integer;
begin
SL := TStringList.Create;
try
ADOConnection1.GetTableNames(SL, False);
for index := 0 to (SL.Count - 1) do begin
Table1.Insert;
Table1.FieldByName('Name').AsString := SL[index];
ADOTable1.TableName := SL[index];

23-10 Guide du développeur


Connexion à des stockages de données ADO

ADOTable1.Open;
Table1.FieldByName('Records').AsInteger :=
ADOTable1.RecordCount;
Table1.Post;
end;
finally
SL.Free;
ADOTable1.Close;
end;
end;

Enumération des procédures stockées disponibles


Pour obtenir une liste de toutes les procédures stockées de la base de données à
laquelle on accède via l’objet connexion, utilisez la méthode GetProcedureNames.
Cette méthode copie une liste des noms de procédures stockées dans un objet
liste de chaînes existant. Utilisez, par exemple, les éléments de cette liste pour
définir la valeur de la propriété ProcedureName d’un composant TADOStoredProc.
ADOConnection1.GetProcedureNames(ListBox1.Items);
Dans l’exemple suivant, la liste des noms de procédures stockées obtenue en
utilisant la méthode GetProcedureNames est utilisée pour exécuter toutes les
procédures stockées de la base de données associée.
procedure TDataForm.ExecuteProcsButtonClick(Sender: TObject);
var
SL: TStrings;
index: Integer;
begin
SL := TStringList.Create;
try
ADOConnection1.GetProcedureNames(SL);
if (SL.Count > 0) then
for index := 0 to (SL.Count - 1) do begin
ADOStoredProc1.ProcedureName := SL[index];
ADOStoredProc1.ExecProc;
end;
finally
SL.Free;
end;
end;

Utilisation des transactions de connexion


Le composant TADOConnection propose plusieurs méthodes et événements
permettant d’utiliser les transactions. Cette gestion des transactions est partagée
par tous les composants commande et ensemble de données ADO qui utilisent la
connexion avec le stockage de données.

Utilisation des composants ADO 23-11


Utilisation des ensembles de données ADO

Utilisation des méthodes de transaction


Utilisez les méthodes BeginTrans, CommitTrans et RollbackTrans pour gérer les
transactions. BeginTrans démarre une transaction dans le stockage de données
associé au composant connexion ADO. CommitTrans valide la transaction en
cours, enregistre les modifications dans la base de données et termine la
transaction. RollbackTrans annule la transaction en cours, abandonne toutes les
modifications effectuées pendant la transaction et termine la transaction. Lisez la
propriété InTransaction pour savoir à tout moment si le composant connexion a
une transaction en cours.
Une transaction démarrée par le composant connexion est partagée par tous les
composants commande et ensemble de données qui utilisent cette connexion.

Utilisation des événements de transaction


Les composants de connexion ADO propose plusieurs événements pour gérer le
cours d’un processus de transaction. Ces événements indiquent pour un
processus de transaction démarré par BeginTrans, quand les méthodes
CommitTrans ou RollbackTrans ont été appelées pour le stockage de données.
L’événement OnBeginTransComplete est déclenché quand le stockage de données a
démarré une connexion après l’appel de la méthode BeginTrans du composant
connexion. L’événement OnCommitTransComplete est déclenché après la validation
réussie d’une connexion à la suite de l’appel de CommitTrans. Et l’événement
OnRollbackTransComplete est déclenché après l’arrêt d’une transaction due à
l’appel de RollbackTrans.

Utilisation des ensembles de données ADO


Les composants ensemble de données ADO de Delphi sont similaires aux
composants ensemble de données utilisant le BDE. Par exemple, le composant
TADOTable est fonctionnellement identique au composant TTable. La principale
différence tient à ce que les composants ensemble de données ADO utilisent des
objets ADO sous-jacents pour leurs accès aux données et pas le moteur de bases
de données Borland (BDE).
Les composants ensemble de données ADO et BDE ont la classe TDataSet comme
ancêtre commun. De ce fait, ils partagent des fonctionnalités communes dans des
propriétés, méthodes et événements hérités ou similaires. Cette section décrit
avant tout les aspects des composants ADO qui différent de ceux des ensembles
de données génériques correspondants. Pour davantage d’informations sur les
fonctionnalités communes à ces deux types de composants ensemble de données,
voir la description des composants ensemble de données génériques ou BDE :
• “Présentation des ensembles de données” à la page 18-1
• “Manipulation des tables” à la page 20-1
• “Manipulation des requêtes” à la page 21-1
• “Manipulation des procédures stockées” à la page 22-1

23-12 Guide du développeur


Utilisation des ensembles de données ADO

Cette section contient des informations sur les fonctionnalités différentes pour la
version ADO des composants ensemble de données par rapport à leurs équivalents
génériques. Ces informations sont proposées dans les sections suivantes :
• Caractéristiques communes à tous les composants ensemble de données ADO
• Utilisation de TADODataSet
• Utilisation de TADOTable
• Utilisation de TADOQuery
• Utilisation de TADOStoredProc

Caractéristiques communes à tous les composants ensemble de


données ADO
Certains aspects des composants ensemble de données ADO fonctionnent à
l’identique pour tous les différents composants. Sauf exception indiquée, ces
fonctionnalités sont mises en oeuvre de la même façon pour tous les composants
ensemble de données ADO utilisables.

Modification des données


L’accès aux colonnes d’un composant ensemble de données ADO et la modification
des données s’effectue exactement comme avec les composants ensemble de
données génériques. Utilisez les méthodes d’ensembles de données comme Edit et
Insert pour placer l’ensemble de données en mode édition avant de modifier les
données. Utilisez la méthode Post pour terminer la modification des données.
Utilisez les références dynamiques de TField spécifiées par la propriété Fields et la
méthode FieldByname des composants ensemble de données. A partir de là,
utilisez les propriétés et méthodes de la classe TField et de ses descendants pour
obtenir ou modifier la valeur d’une colonne, valider les données ou déterminer
le type de données d’une colonne.
Pour davantage d’informations sur la modification des données d’un ensemble
de données, voir “Modification des données” à la page 18-24. Pour davantage
d’informations sur l’utilisation des colonnes de table et des objets champ
persistant, voir “Manipulation des composants champ” à la page 19-1.

Déplacement dans un ensemble de données


Les déplacements dans un ensemble de données ADO s’effectuent de la même
façon que dans les composants ensemble de données génériques. Utilisez des
méthodes comme First, Next, Last et Prior pour déplacer le pointeur
d’enregistrement d’une ligne à l’autre du composant ensemble de données. Il est
possible d’utiliser une boucle en exploitant les propriétés Eof et Bof afin d’agir
sur toutes les lignes d’un ensemble de données.
ADOTable1.First;
while not ADOTable1.Eof do begin
{ Traiter chaque ligne ici }
ƒ
ADOTable1.Next;
end;

Utilisation des composants ADO 23-13


Utilisation des ensembles de données ADO

Pour davantage d’informations sur le parcours dans les lignes d’un composant
ensemble de données, voir “Navigation dans les ensembles de données” à la
page 18-10.

Utilisation des contrôles orientés données


Vous pouvez, dans une application, utiliser l’ensemble de données fourni par un
composant ensemble de données ADO avec des contrôles orientés données. Cela
vaut aussi bien pour les lignes renvoyées par un composant TADOTable, pour
l’ensemble de résultats renvoyés par une instruction SELECT d’un composant
TADOQuery et pour les procédures stockées qui renvoient un ensemble de
résultats exécutées depuis un composant TADOStoredProc.
Pour accéder à ces ensembles de données dans des contrôles orientés données :
1 Utilisez un composant TDataSource standard.
2 Spécifiez dans sa propriété DataSet un composant ensemble de données ADO
actif.
3 Utilisez des contrôles orientés données standard comme TDBEdit ou TDBGrid.
4 Spécifiez le TDataSource dans la propriété DataSource du contrôle orienté
données.
Vous pouvez, par exemple, créer par code cette relation entre le composant
ensemble de données ADO, le composant source de données et le contrôle
orienté données :
DBGrid1.DataSource := DataSource1;
DataSource1.DataSet := ADOQuery1;
ADOQuery1.Open;

Connexion à un stockage de données en utilisant des composants


ensemble de données ADO
Il est possible de connecter individuellement ou collectivement des composants
ensemble de données ADO à un stockage de données ADO.
Quand vous connectez des composants ensemble de données collectivement,
initialisez la propriété Connection de chaque composant ensemble de données
avec un composant TADOConnection. Chaque composant ensemble de données
utilise alors la connexion établie par le composant connexion.
ADODataSet1.Connection := ADOConnection1;
ADODataSet2.Connection := ADOConnection1;
...
Cette manière de procéder présente, entre autres, les avantages suivants :
• Les composants ensemble de données partagent les attributs de l’objet
connexion.
• Il n’y a qu’une seule connexion à configurer : celle de l’objet TADOConnection.
• Les composants ensemble de données peuvent participer à des transactions.

23-14 Guide du développeur


Utilisation des ensembles de données ADO

Pour connecter individuellement des composants ensemble de données, initialisez


la propriété ConnectionString de chaque composant ensemble de données. Les
informations nécessaires à la connexion avec le stockage de données doivent être
définies pour chaque composant ensemble de données. Chaque composant
ensemble de données établit sa propre connexion avec le stockage de données,
de manière entièrement indépendante des autres connexions d’ensembles de
données de l’application.
ADODataSet1.ConnectionString := 'Provider=YourProvider;Password=SecretWord;' +
'User ID=JaneDoe;SERVER=PURGATORY;UID=JaneDoe;PWD=SecretWord;' +
'Initial Catalog=Employee';
ADODataSet2.ConnectionString := 'Provider=YourProvider;Password=SecretWord;' +
'User ID=JaneDoe;SERVER=PURGATORY;UID=JaneDoe;PWD=SecretWord;' +
'Initial Catalog=Employee';
...
Pour davantage d’informations sur l’utilisation de TADOConnection pour se
connecter à un stockage de données, voir “Connexion à un stockage de données
avec TADOConnection” à la page 23-3.

Utilisation des ensembles d’enregistrements


Outre les méthodes de déplacement dans les enregistrements ou de modification
des données qui sont partagées par tous les composants ensemble de données
ADO, beaucoup d’autres propriétés et méthodes agissent sur les ensembles
d’enregistrements.
La propriété RecordSet donne un accès direct à l’objet ensemble d’enregistrements
ADO sous-jacent au composant ensemble de données. En utilisant cet objet, il est
possible dans une application d’accéder aux propriétés et d’appeler les méthodes
de l’objet ensemble d’enregistrements. L’utilisation de RecordSet pour accéder
directement à l’objet ensemble d’enregistrements ADO sous-jacent suppose une
bonne connaissance des objets ADO en général et de l’objet ensemble
d’enregistrements ADO en particulier. Il n’est pas conseillé d’utiliser directement
l’objet ensemble d’enregistrements à moins d’être familiarisé avec les opérations
sur l’objet ensemble d’enregistrements. Consultez l’aide du SDK Microsoft Data
Access pour des informations spécifiques sur les objets ensemble
d’enregistrements.
Utilisez la propriété RecordSetState pour déterminer l’état en cours du composant
ensemble de données. RecordSetState implémente la propriété State de l’objet
ensemble d’enregistrement ADO et reflète ainsi l’état en cours de l’objet
ensemble d’enregistrements sous-jacent. La propriété RecordSetState contient l’une
des valeurs suivantes : stExecuting ou stFetching. La valeur stExecuting indique
que le composant ensemble de données est en train d’exécuter une commande.
La valeur stFetching indique que le composant ensemble de données est en train
de lire des lignes dans les tables associées.
Utilisez ces valeurs quand vous effectuez des actions qui dépendent de l’état en
cours de l’ensemble de données. Ainsi, une routine qui actualise les données
peut tester la valeur de la propriété RecordSetState pour vérifier si l’ensemble de
données est actif et n’est pas en train de traiter d’autres opérations ou de lire des
données.

Utilisation des composants ADO 23-15


Utilisation des ensembles de données ADO

Utilisation des mises à jour groupées


Les composants ensemble de données ADO permettent de placer dans un cache
les modifications apportées à l’ensemble de données, puis d’appliquer toutes les
modifications dans une opération groupée ou d’annuler une ou toutes les
modifications. Les mises à jour groupées offrent une forme de contrôle de
transaction mais au niveau du composant ensemble de données. Normalement,
les transactions sont gérées par les méthodes du composant connexion ADO.
L’utilisation des caractéristiques de mises à jour groupées des composants
ensemble de données ADO fait intervenir :
• Ouverture de l’ensemble de données en mode mises à jour groupées
• Examen du statut de mise à jour ligne par ligne
• Filtrage de lignes en fonction du statut de mise à jour
• Application des mises à jour groupées dans les tables des bases
• Annulation des mises à jour groupées

Ouverture de l’ensemble de données en mode mises à jour groupées


Pour ouvrir un ensemble de données ADO en mode mises à jour groupées, il
doit respecter les critères suivants :
1 La propriété CursorType du composant doit avoir la valeur ctKeySet (valeur par
défaut de la propriété) ou ctStatic.
2 La propriété LockType doit avoir la valeur ltBatchOptimistic.
3 La commande doit être une requête SELECT.
Avant d’activer le composant ensemble de données, initialisez les propriétés
CursorType et LockType avec les valeurs indiquées ci-dessus. Affectez une
instruction SELECT à la propriété CommandText du composant (pour
TADODataSet) ou à la propriété SQL (pour TADOQuery). Pour les composants
TADOStoredProc, affectez à la propriété ProcedureName le nom d’une procédure
stockée qui renvoie un ensemble de résultats. Ces propriétés peuvent être
définies à la conception en utilisant l’inspecteur d’objet ou par code à l’exécution.
L’exemple suivant illustre la préparation d’un composant TADODataSet en mode
mises à jour groupées.
with ADODataSet1 do begin
CursorLocation := clUseServer;
CursorType := ctKeyset;
LockType := ltBatchOptimistic;
CommandType := cmdText;
CommandText := 'SELECT * FROM Employee';
Open;
end;
Une fois l’ensemble de données ouvert en mode mises à jour groupées, toutes les
modifications des données sont placées dans le cache au lieu d’être appliquées
directement aux tables de la base.

23-16 Guide du développeur


Utilisation des ensembles de données ADO

Examen du statut de mise à jour ligne par ligne


Pour déterminer le statut de la mise à jour pour une ligne donnée, faites-en la
ligne en cours puis inspectez la propriété RecordStatus du composant de données
ADO. RecordStatus indique l’état de mise à jour pour la ligne en cours et
uniquement pour elle.
case ADOQuery1.RecordStatus of
rsUnmodified: StatusBar1.Panels[0].Text := 'Enregistrement non modifié';
rsModified: StatusBar1.Panels[0].Text := 'Enregistrement modifié';
rsDeleted: StatusBar1.Panels[0].Text := 'Enregistrement supprimé';
rsNew: StatusBar1.Panels[0].Text := 'Enregistrement ajouté';
end;

Filtrage de lignes en fonction du statut de mise à jour


Le filtrage d’un ensemble d’enregistrements ne montre que les lignes qui
appartiennent au groupe des lignes ayant le statut de mise à jour avec la valeur
spécifiée par la propriété FilterGroup. Affectez à FilterGroup les constantes de type
TFilterGroup qui représentent le statut de mise à jour des lignes à afficher. La
valeur fgNone (valeur par défaut de cette propriété) spécifie une absence de
filtrage et l’affichage de toutes les lignes sans tenir compte du statut de mise à
jour (sauf les lignes marquées pour effacement). L’exemple suivant n’affiche que
les lignes mises à jour en attente.
FilterGroup := fgPendingRecords;
Filtered := True;
Pour que la propriété FilterGroup prenne effet, la propriété Filtered du composant
ensemble de données ADO doit avoir la valeur True.

Application des mises à jour groupées dans les tables des bases
Pour appliquer les mises à jour en attente qui n’ont pas déjà été appliquées ou
annulées, appelez la méthode UpdateBatch. Pour les lignes dont le contenu a
changé et qui sont appliquées, les modifications sont placées dans les tables de
base sur lesquelles l’ensemble d’enregistrement est basé. Une ligne du cache
marqué pour la suppression entraîne la suppression de la ligne correspondante
de la table de la base. Une insertion d’enregistrement (l’enregistrement existe
dans le cache mais pas dans la table de la base) est ajoutée à la table de la base.
Pour les lignes modifiées, les colonnes des lignes correspondantes des tables de
la base sont modifiées pour adopter les nouvelles valeurs de colonnes présentes
dans le cache.
Utilisée sans paramètre, la méthode UpdateBatch applique toutes les mises à jour
en attente. Il est possible de transmettre une valeur de type TUpdateBatchOptions
comme paramètre de UpdateBatch. Si une valeur autre que ubAffectAll est
transmise, seul un sous-ensemble des modifications en attente est appliqué. Le
paramètre ubAffectAll a le même effet que l’absence de paramètre et provoque
l’application de toutes les mises à jour en attente. L’exemple suivant n’applique
que la ligne en cours :
ADODataSet1.UpdateBatch(ubAffectCurrent);

Utilisation des composants ADO 23-17


Utilisation des ensembles de données ADO

Annulation des mises à jour groupées


Pour annuler les mises à jour en attente, appelez la méthode CancelBatch. Dans
les lignes modifiées qui sont alors annulées, la valeur des colonnes revient à celle
existant avant le dernier appel de CancelBatch ou de UpdateBatch, si l’une de ces
méthodes a déjà été appelée ou avant le groupe de modifications en attente.
Utilisé sans paramètre, CancelBatch annule toutes les mises à jour en attente. Il
est possible de transmettre une valeur de type TUpdateBatchOptions comme
paramètre de CancelBatch. Si une valeur autre que ubAffectAll est transmise, seul
un sous-ensemble des modifications en attente est annulé. Le paramètre
ubAffectAll a le même effet que l’absence de paramètre et provoque l’annulation
de toutes les mises à jour en attente. L’exemple suivant annule toutes les
modifications en attente :
ADODataSet1.Cancel;

Lecture et enregistrement des données dans des fichiers


Les données obtenues dans un composant ensemble de données ADO peuvent être
enregistrées dans un fichier pour une utilisation ultérieure dans le même ordinateur
ou dans un autre. Enregistrez les données dans un fichier en utilisant la méthode
SaveToFile. Récupérez les données enregistrées en utilisant la méthode LoadFromFile.
Les données sont enregistrées en utilisant un des deux formats propriétaire
proposés : ADTG ou XML. Indiquez lequel de ces formats vous souhaitez utiliser
pour enregistrer le fichier à l’aide de l’une des constantes TPersistFormat : pfADTG
ou pfXML dans le paramètre Format de la méthode SaveToFile.
Dans l’exemple suivant, la première procédure enregistre dans un fichier
l’ensemble de données obtenu par le composant TADODataSet ADODataSet1. Le
fichier destination est un fichier ADTG nommé SaveFile qui est créé sur un
disque local. La seconde procédure charge ce fichier dans le composant
TADODataSet ADODataSet2.
procedure TForm1.SaveBtnClick(Sender: TObject);
begin
if (FileExists('c:\SaveFile')) then begin
DeleteFile('c:\SaveFile');
StatusBar1.Panels[0].Text := 'Fichier sauvegarde supprimé!';
end;
ADODataSet1.SaveToFile('c:\SaveFile', pfADTG);
end;
procedure TForm1.LoadBtnClick(Sender: TObject);
begin
if (FileExists('c:\SaveFile')) then
ADODataSet2.LoadFromFile('c:\SaveFile')
else
StatusBar1.Panels[0].Text := 'Fichier sauvegarde inexistant!';
end;
Il n’est pas nécessaire d’effectuer la sauvegarde et la lecture des composants
ensemble de données dans la même fiche comme dans l’exemple précédent, ni
dans la même application ou sur la même machine. Cela permet des transferts
de données de style porte-documents d’un ordinateur à un autre.

23-18 Guide du développeur


Utilisation des ensembles de données ADO

Lors de l’appel de la méthode LoadFromFile, le composant ensemble de données


est automatiquement activé.
Si le fichier spécifié par le paramètre FileName de la méthode SaveToFile existe
déjà, une exception EOleException est déclenchée. De même, si le fichier spécifié
par le paramètre FileName de LoadFromFile n’existe pas, une exception
EOleException est déclenchée.
Les deux formats de fichiers ADTG et XML sont les seuls formats gérés par
ADO. De plus, ces deux formats ne sont pas nécessairement gérés par toutes les
versions de ADO. Consultez la documentation ADO pour connaître quels
formats de fichiers gèrent la version en cours.

Utilisation de paramètres dans des commandes


Pour utiliser des paramètres dans les commandes et instructions SQL exécutées
comme commandes en utilisant des composants ensemble de données ADO,
vous devez :
1 Inclure les paramètres dans l’instruction SQL (identifiés par le préfixe deux-
points).
2 Initialiser les valeurs de propriété pour chaque composant TParameter.
Pour chaque élément de l’instruction SQL identifié comme un paramètre, un
composant TParameter est automatiquement créé et ajouté à la propriété
Parameters du composant ensemble de données (tableau TParameters de
composants TParameter). A la conception, accédez aux composants paramètre
pour définir leur valeur en utilisant l’éditeur de propriété de la propriété
Parameters. Pour appeler cet éditeur de propriété, cliquez sur le bouton points de
suspension de la propriété Parameters dans l’inspecteur d’objet.
A l’exécution, accédez aux composants paramètres pour obtenir ou modifier leur
valeur en utilisant la propriété Parameters du composant ensemble de données.
Spécifiez un indice dans Parameters qui indique la position (relativement aux
autres paramètres) d’un paramètre spécifique dans l’instruction SQL. Cet indice
est de base zéro, le premier paramètre est référencé par l’indice zéro, le second
par l’indice un, etc. Vous pouvez également utiliser la référence TParameters
fournie par la propriété Parameters et appeler sa méthode ParamByName afin de
désigner le paramètre par son nom.
{ référence au premier paramètre par indice }
ADOQuery1.Parameters[0].Value := 'téléphone';
{ référence à un paramètre par son nom }
ADOQuery1.Parameters.ParamByName('Amount').Value := 123;
Dans l’exemple suivant, l’instruction SQL suivante est utilisée dans un
composant TADOQuery.
SELECT CustNo, Company, State
FROM CUSTOMER
WHERE (State = :StateParam)

Utilisation des composants ADO 23-19


Utilisation des ensembles de données ADO

Cette instruction contient un seul paramètre : StateParam. La routine suivante


ferme le composant requête ADO, initialise la valeur du paramètre StateParam
via la propriété Parameters puis ouvre de nouveau le composant requête ADO.
La propriété Parameters nécessite l’identification d’un paramètre par un numéro
représentant la position du paramètre dans l’instruction par rapport aux autres
paramètres. Parameters est de base zéro, le premier paramètre est donc référencé
par l’indice zéro, le second par l’indice un, etc. Comme StateParam est le premier
paramètre de l’instruction, il faut utiliser l’indice zéro pour l’identifier.
procedure TForm1.GetCaliforniaBtnClick(Sender: TObject);
begin
with ADOQuery1 do begin
Close;
Parameters[0].Value := 'CA';
Open;
end;
end;
La procédure suivante fait la même chose mais en utilisant la méthode
TParameters.ParamByName pour spécifier la valeur du paramètre. La méthode
ParamByName nécessite l’identification du paramètre par le nom utilisé dans
l’instruction SQL (sans le signe deux-points).
procedure TForm1.GetFloridaBtnClick(Sender: TObject);
begin
with ADOQuery1 do begin
Close;
Parameters.ParamByName('StateParam').Value := 'FL';
Open;
end;
end;

Utilisation de TADODataSet
Le composant TADODataSet permet aux applications Delphi d’accéder aux
données d’une ou de plusieurs tables d’une base de données via ADO. Les tables
consultées sont spécifiées en utilisant la propriété CommandText du composant
ensemble de données ADO pour indiquer leur nom ou une instruction SQL.
L’accès à la base de données s’effectue par l’intermédiaire d’une connexion à un
stockage de données établie par le composant ensemble de données ADO grâce à
sa propriété ConnectionString ou grâce à un composant TADOConnection distinct
spécifié par la propriété Connection. Pour davantage d’informations, voir
“Connexion à un stockage de données en utilisant des composants ensemble de
données ADO” à la page 23-14.
L’utilisation des données fournies par un composant TADODataSet dans des
contrôles visuels, le parcours des lignes et la modification par code des données
se font de la même manière que pour les autres composants ensemble de
données ADO. Pour davantage d’informations sur les caractéristiques communes
à tous les composants ensemble de données, voir “Caractéristiques communes à
tous les composants ensemble de données ADO” à la page 23-13.

23-20 Guide du développeur


Utilisation des ensembles de données ADO

Utilisation d’une commande pour obtenir un ensemble de données


Le composant TADODataSet est capable d’obtenir les données d’une seule table
en utilisant le nom d’une table. Il peut également obtenir des données d’une ou
de plusieurs tables en utilisant une instruction SQL. Dans l’un ou l’autre cas, le
nom de la table ou l’instruction SQL sont exécutés comme une commande.
Spécifiez le nom d’une table ou une instruction SQL dans la propriété
CommandText et activez le composant. A la conception, vous pouvez utiliser
l’éditeur de texte de commande pour concevoir la commande. Pour appeler cet
éditeur, cliquez sur le bouton points de suspension de la propriété CommandText
dans l’inspecteur d’objet. A l’exécution affectez une commande à CommandText
sous la forme d’une valeur String.
ADODataSet1.CommandText := 'SELECT * FROM Customer';
Utilisez la propriété CommandType pour indiquer le type de commande exécutée :
cmdTable (ou cmdTableDirect) si la commande est un nom de table et cmdText pour
les instructions SQL. Vous pouvez également spécifier cmdUnknown si le type de
commande est inconnu lors de l’exécution ou si vous voulez que ADO devine le
type de commande en se basant sur le contenu de CommandText. A la conception,
sélectionnez la valeur souhaitée pour CommandType dans la liste déroulante de
l’inspecteur d’objet. A l’exécution, affectez une valeur de type TCommandType.
ADODataSet1.CommandType := cmdText;
Activez le TADODataSet en appelant sa méthode Open ou en affectant la valeur
True à sa propriété Active.
with ADODataSet1 do begin
Connection := ADOConnection1;
CommandType := cmdText;
CommandText := 'SELECT * FROM Customer';
Open;
end;

Utilisation de TADOTable
Le composant TADOTable permet aux applications Delphi d’accéder aux données
d’une seule table d’une base de données via ADO. Cette table est spécifiée par la
propriété TableName du composant table ADO.
L’accès à la base de données s’effectue par l’intermédiaire d’une connexion à un
stockage de données établie par le composant table ADO grâce à sa propriété
ConnectionString ou grâce à un composant TADOConnection distinct spécifié par
la propriété Connection. Pour davantage d’informations, voir “Connexion à un
stockage de données en utilisant des composants ensemble de données ADO” à
la page 23-14.
L’utilisation des données fournies par un composant TADOTable dans des
contrôles visuels, le parcours des lignes et la modification par code des données
se font de la même manière que pour les autres composants ensemble de
données ADO. Pour davantage d’informations sur les caractéristiques communes

Utilisation des composants ADO 23-21


Utilisation des ensembles de données ADO

à tous les composants ensemble de données, voir “Caractéristiques communes à


tous les composants ensemble de données ADO” à la page 23-13.

Spécification de la table à utiliser


Dès qu’un composant TADOTable dispose d’une connexion avec une base de
données, il peut accéder aux tables contenues dans la base de données. Spécifiez
la table de la base de données en utilisant la propriété TableName. Quand le
composant table ADO est activé, la table et ses données deviennent accessibles
via le composant TADOTable.
A la conception, si le composant TADOTable dispose d’une connexion
fonctionnelle avec un stockage de données, l’éditeur de la propriété TableName
énumère le nom des tables disponibles. Sélectionnez l’une des tables de la liste.
A l’exécution, affectez une valeur String contenant un nom de table à la
propriété TableName.
ADOTable1.TableName := 'Orders';
Si un composant TADOConnection est utilisé pour se connecter au stockage de
données, vous pouvez utiliser sa méthode GetTableNames pour obtenir la liste des
tables disponibles. GetTableNames remplit un objet liste de chaînes existant avec
le nom des tables disponibles via la connexion.
Ainsi, la première routine suivante remplit un composant TListBox nommé
ListBox1 avec le nom des tables disponibles via la connexion TADOConnection
nommée ADOConnection1. La seconde routine est un gestionnaire de l’événement
OnDblClick de ListBox1. Dans ce gestionnaire d’événement, le nom de table
actuellement sélectionné dans ListBox1 est affecté à la propriété TableName du
composant TADOTable nommé ADOTable1. Le composant table ADO est alors
activé.
procedure TForm1.ListTablesButtonClick(Sender: TObject);
begin
ADOConnection1.GetTableNames(ListBox1.Items, False);
end;
procedure TForm1.ListBox1DblClick(Sender: TObject);
begin
with ADOTable1 do begin
Close;
TableName := (Sender as TListBox).Items[(Sender as TListBox).ItemIndex];
Open;
end;
end;

Utilisation de TADOQuery
Le composant TADOQuery permet aux applications Delphi d’accéder aux
données d’une ou de plusieurs tables d’une base de données ADO en utilisant le
SQL. Spécifiez l’instruction SQL à utiliser par le composant requête ADO dans la
propriété SQL. TADOQuery peut renvoyer des données en utilisant le langage de
manipulation de données (DML) ou créer et modifier des objets de métadonnées

23-22 Guide du développeur


Utilisation des ensembles de données ADO

en utilisant le langage de définition de données (DDL). La syntaxe SQL utilisée


dans un composant TADOQuery doit être reconnue par le pilote ADO utilisé.
Delphi n’effectue aucune évaluation du code SQL et ne l’exécute pas.
L’instruction SQL est juste transmise à la base de données distante pour
exécution. Si l’instruction SQL produit un ensemble de résultats, il est transmis
par la base de données à Delphi dans le TADOQuery pour être utilisé par
l’application.
L’accès à la base de données s’effectue par l’intermédiaire d’une connexion à un
stockage de données établie par le composant requête ADO grâce à sa propriété
ConnectionString ou grâce à un composant TADOConnection distinct spécifié par
la propriété Connection. Pour davantage d’informations, voir “Connexion à un
stockage de données en utilisant des composants ensemble de données ADO” à
la page 23-14.
L’utilisation des données fournies par un composant TADOQuery dans des
contrôles visuels, le parcours des lignes et la modification par code des données
se font de la même manière que pour les autres composants ensemble de
données ADO. Pour davantage d’informations sur les caractéristiques communes
à tous les composants ensemble de données, voir “Caractéristiques communes à
tous les composants ensemble de données ADO” à la page 23-13.

Spécification des instructions SQL


A la conception, appelez l’éditeur de la propriété SQL en cliquant sur le bouton
points de suspension dans l’inspecteur d’objet. Dans la boîte de dialogue de
l’éditeur, entrez l’instruction SQL du TADOQuery.
A l’exécution, affectez une valeur à la propriété SQL. Comme pour un
composant requête standard, TQuery, la propriété TADOQuery.SQL est un objet
liste de chaînes. Utilisez les propriétés et méthodes de la classe liste de chaînes
pour affecter la valeur de la propriété SQL.
Dans l’exemple suivant, une instruction SELECT est affectée à la propriété SQL
d’un composant TADOQuery nommé ADOQuery1.
with ADOQuery1 do begin
Close;
with SQL do begin
Clear;
Add('SELECT Company, State');
Add('FROM CUSTOMER');
Add('WHERE State = ' + QuotedStr('HI'));
Add('ORDER BY Company');
end;
Open;
end;

Exécution d’instructions SQL


Il est possible d’exécuter de deux manières différentes un TADOQuery contenant
une instruction SQL correcte dans sa propriété SQL. La méthode employée
dépend du type d’instruction SQL qu’exécute le composant requête ADO.

Utilisation des composants ADO 23-23


Utilisation des ensembles de données ADO

Si l’instruction SQL renvoie un ensemble de résultats, le composant requête ADO


doit être activé en appelant sa méthode Open ou en initialisant à True sa
propriété Active. Seules les instructions SELECT renvoient un ensemble de
résultats, donc un TADOQuery contenant une instruction SELECT dans sa
propriété SQL doit toujours être activé ainsi.
ADOQuery1.SQL.Text := 'SELECT * FROM TrafficViolations';
ADOQuery1.Open;
Comme il n’est pas possible d’appeler des méthodes lors de la conception d’une
application dans l’EDI Delphi, seule la propriété Active peut être utilisée pour
activer ce type de requête à la conception. Fonctionnellement, cela donne le
même résultat que l’appel de la méthode Open (à l’exécution).
L’exécution d’une instruction SQL qui ne renvoie pas un ensemble de résultats
s’effectue en appelant la méthode ExecSQL du composant TADOQuery. Toutes les
instructions SQL, à l’exception de SELECT, rentrent dans cette catégorie :
INSERT, DELETE, UPDATE, CREATE INDEX, ALTER TABLE, etc. Un
composant TADOQuery contenant l’une de ces instructions SQL dans sa
propriété SQL doit toujours être activé de cette manière.
ADOQuery1.SQL.Text := 'DELETE FROM TrafficViolations WHERE (TicketID = 1099)';
ADOQuery1.ExecSQL;
Le composant TADOCommand permet d’exécuter des instructions SQL, qui
comme la précédente, ne renvoient pas d’ensemble de résultats.

Utilisation de TADOStoredProc
Le composant TADOStoredProc permet aux applications Delphi d’exécuter des
procédures stockées d’une base de données à laquelle on accède en utilisant un
stockage de données ADO. Pour spécifier la procédure stockée à exécuter,
utilisez la propriété ProcedureName du composant procédure stockée ADO.
L’accès à la base de données s’effectue par l’intermédiaire d’une connexion à un
stockage de données établie par le composant procédure stockée ADO grâce à sa
propriété ConnectionString ou grâce à un composant TADOConnection distinct
spécifié par la propriété Connection. Pour davantage d’informations, voir
“Connexion à un stockage de données en utilisant des composants ensemble de
données ADO” à la page 23-14.
Les ensembles de résultats d’un composant TADOStoredProc s’utilisent dans une
application de la même manière que ceux des composants requête TStoredProc
standard utilisant le BDE. Utilisez le composant procédure stockée ADO pour
spécifier la propriété DataSet d’un composant TDataSource standard. Le
composant TDataSource se comporte ensuite comme un canal transférant les
données entre le composant procédure stockée ADO et les contrôles orientés
données. Pour davantage d’informations, voir “Utilisation des contrôles orientés
données” à la page 23-14.

23-24 Guide du développeur


Utilisation des ensembles de données ADO

L’utilisation des données fournies par un composant TADOStoredProc dans des


contrôles visuels, le parcours des lignes et la modification par code des données
se font de la même manière que pour les autres composants ensemble de
données ADO. Pour davantage d’informations sur les caractéristiques communes
à tous les composants ensemble de données, voir “Caractéristiques communes à
tous les composants ensemble de données ADO” à la page 23-13.

Spécification de la procédure stockée


Dès qu’un composant TADOStoredProc dispose d’une connexion fonctionnelle
avec une base de données, il peut exécuter les procédures stockées contenues
dans la base de données. Spécifiez le nom d’une procédure stockée de la base de
données en utilisant la propriété ProcedureName. Activez le composant procédure
stockée ADO en utilisant sa méthode Open si elle renvoie un ensemble de
résultats, ou sinon sa méthode ExecProc.
A la conception, si le composant TADOStoredProc dispose d’une connexion
fonctionnelle avec un stockage de données, l’éditeur de la propriété
ProcedureName énumère le nom des procédures stockées disponibles. Sélectionnez
l’une des procédures stockées de la liste. A l’exécution, affectez à la propriété
ProcedureName une valeur String contenant un nom de procédure stockée.
ADOStoredProc1.ProcedureName := 'DeleteEmployee';
Si un composant TADOConnection est utilisé pour se connecter au stockage de
données, vous pouvez utiliser sa méthode GetProcedureNames pour obtenir une
liste des procédures stockées disponibles. GetProcedureNames remplit une liste de
chaînes existante avec le nom des procédures stockées disponibles via la
connexion.
Par exemple, la première routine suivante remplit un composant TListBox
nommé ListBox1 avec le nom des procédures stockées disponibles via le
composant TADOConnection nommé ADOConnection1. La seconde routine est un
gestionnaire de l’événement OnDblClick de ListBox1. Dans ce gestionnaire
d’événement, le nom de procédure stockée sélectionné dans ListBox1 est affecté à
la propriété ProcedureName du composant TADOStoredProc nommé
ADOStoredProc1. Le composant ADO est ensuite exécuté en utilisant sa méthode
ExecProc.
procedure TForm1.ListProceduresButtonClick(Sender: TObject);
begin
ADOConnection1.GetProcedureNames(ListBox1.Items);
end;
procedure TForm1.ListBox1DblClick(Sender: TObject);
begin
with ADOStoredProc1 do begin
ProcedureName := TListBox(Sender).Items[TListBox(Sender).ItemIndex];
ExecProc;
end;
end;

Utilisation des composants ADO 23-25


Utilisation des ensembles de données ADO

Exécution d’une procédure stockée


Un composant TADOStoredProc dont la propriété ProcedureName contient le nom
d’une procédure stockée existante peut être exécuté de deux manières différentes.
La manière d’exécuter une procédure varie selon qu’elle renvoie ou pas un
ensemble de résultats.
Si la procédure stockée renvoie un ensemble de résultats, le composant
procédure stockée ADO doit être activé en appelant sa méthode Open ou en
affectant la valeur True à sa propriété Active.
ADOStoredProc1.ProcedureName := 'ShowPurebreds';
ADOStoredProc1.ExecProc;
Comme il n’est pas possible d’appeler des méthodes lors de la conception d’une
application dans l’EDI Delphi, seule la propriété Active peut être utilisée pour
activer ce type de procédure stockée à la conception.
L’exécution d’une procédure stockée ne renvoyant pas d’ensemble de résultats
est obtenue en appelant la méthode ExecProc du composant TADOStoredProc.
Toutes les instructions SQL, à l’exception de SELECT, rentrent dans cette
catégorie : INSERT, DELETE, UPDATE, CREATE INDEX, ALTER TABLE, etc.
ADOStoredProc1.ProcedureName := 'DeletePoodles';
ADOStoredProc1.ExecProc;

Utilisation des paramètres de procédures stockées


Le composant TADOStoredProc peut gérer trois types de paramètres (mais toutes
les bases de données n’acceptent pas nécessairement ces types). Un paramètre
peut être utilisé en entrée, en sortie ou pour renvoyer un ensemble de résultats.
Cette section décrit l’utilisation de paramètres de procédures stockées ayant ces
trois rôles. Il est possible d’utiliser un paramètre pour deux rôles à la fois, par
exemple comme paramètre d’entrée et comme paramètre de sortie. C’est juste
une variation sur les trois rôles de base. L’utilisation d’un paramètre pour jouer
deux rôles nécessite simplement de combiner des approches fonctionnelles
décrites ci-dessous.
Le rôle (on parle également de la direction) d’un paramètre est défini dans la
procédure stockée lors de sa création. Cette direction ne peut pas être modifiée
ultérieurement par une application frontale. Il n’est donc pas possible d’utiliser
du code Delphi pour changer un paramètre d’entrée en un paramètre de sortie.
Il faut supprimer puis recréer la procédure stockée en donnant au paramètre un
nouveau rôle. La direction d’un paramètre donné est spécifiée par la propriété
TParameter.Direction, qui peut être lue à la conception dans l’inspecteur d’objet ou
par code à l’exécution.

Tableau 23.2 Propriété Direction d’un paramètre


Direction du paramètre Rôle
pdInput Le paramètre est utilisé pour fournir une valeur à la procédure
stockée avant son exécution.
pdOutput Le paramètre est utilisé par la procédure stockée pour renvoyer
une seule valeur après son exécution.

23-26 Guide du développeur


Utilisation des ensembles de données ADO

Tableau 23.2 Propriété Direction d’un paramètre (suite)


Direction du paramètre Rôle
pdInputOutput Le paramètre peut être utilisé comme paramètre d’entrée et de
sortie, en suivant les définitions précédentes.
pdReturnValue Le paramètre contient un ensemble de résultats après l’exécution.
pdUnknown Paramètre dont la direction n’a pu être déterminée à ce stade de
l’évaluation.

Utilisation des paramètres d’entrée de TADOStoredProc


Utilisez un paramètre d’entrée pour fournir une valeur à une procédure stockée
avant son exécution. Généralement de telles valeurs sont utilisées dans la clause
WHERE de l’instruction SQL d’une procédure stockée afin de restreindre les
lignes des tables concernées. Affectez une valeur au paramètre avant d’activer le
composant TADOStoredProc en appelant sa méthode Open ou en initialisant sa
propriété Active à True (pour les procédures stockées qui renvoient un ensemble
de résultats) ou avant d’exécuter le TADOStoredProc avec sa méthode ExecProc.
A la conception, utilisez l’inspecteur d’objet pour accéder aux paramètres. La
propriété Parameters sélectionnée, cliquez sur le bouton points de suspension.
Cela ouvre la boîte de dialogue de l’éditeur de paramètres. Entrez une valeur de
type appropriée dans la propriété Value.
A l’exécution, affectez une valeur à la propriété Value du composant TParameter
correspondant au paramètre qui vous concerne. Utilisez la référence au
TParameter fournie par la propriété Parameters de TADOStoredProc.
with ADOStoredProc1 do begin
Close;
Parameters[1].Value := 1;
Open;
end;

Utilisation des paramètres de sortie de TADOStoredProc


Utilisez un paramètre de sortie pour récupérer une seule valeur renvoyée par
une procédure stockée. Alors qu’un ensemble de résultats peut être composé de
plusieurs lignes contenant plusieurs colonnes, un paramètre de sortie doit être
l’équivalent d’une seule ligne constituée d’une seule colonne. Si la procédure
stockée utilise une instruction SELECT pour obtenir cette valeur, toute tentative
de renvoyer plusieurs valeurs déclenche une exception.
Un paramètre de sortie ne contient sa valeur qu’après l’activation ou l’exécution
de la procédure stockée. Attention, certains systèmes de bases de données ne
peuvent pas renvoyer à la fois un ensemble de résultats et des paramètres de
sortie. Consultez la documentation de votre système de bases de données pour
déterminer ses possibilités sur ce point. Quand le système de bases de données
peut renvoyer un ensemble de résultats et des paramètres de sortie, le
composant TADOStoredProc peut être activé en utilisant sa méthode Open (ou sa
propriété Active) ou sa méthode ExecProc. Sinon, vous ne pouvez utiliser des
paramètres de sortie pour obtenir des valeurs qu’en utilisant la méthode
ExecProc.

Utilisation des composants ADO 23-27


Exécution de commandes

A la conception, utilisez l’inspecteur d’objet pour accéder aux paramètres. La


propriété Parameters sélectionnée, cliquez sur le bouton points de suspension.
Cela ouvre la boîte de dialogue de l’éditeur de paramètres. Si le composant
TADOStoredProc est activé en utilisant sa propriété Active (le seul moyen de
l’activer à la conception), inspectez la propriété Value de paramètre de sortie
pour connaître la valeur renvoyée.
A l’exécution, affectez une valeur à la propriété Value du composant TParameter
correspondant au paramètre qui vous concerne. Utilisez la référence au TParameter
fournie par la propriété Parameters de TADOStoredProc. Par exemple, la routine
suivante renvoie une valeur String via le paramètre de sortie nommé @OutParam1.
with ADOStoredProc1 do begin
Close;
ShowMessage(VarToStr(Parameters.ParamByName('@OutParam1').Value));
ExecProc;
end;

Utilisations de paramètres valeur de retour de TADOStoredProc


Il n’est pas nécessaire d’accéder directement aux paramètres valeur de retour.
Vous devez à la place accéder à l’ensemble de résultats renvoyé par un paramètre
valeur de retour comme vous le feriez avec n’importe quel ensemble de données.
Pour que l’ensemble de résultats soit accessible à des contrôles orientés données,
utilisez une référence au composant TADOStoredProc comme valeur de la
propriété DataSet d’un composant TDataSource. Le composant TDataSource se
comporte alors comme un intermédiaire entre le composant TADOStoredProc et
les contrôles orientés données. A la conception, utilisez l’inspecteur d’objet : dans
la propriété DataSet de TDataSource, sélectionnez le composant TADOStoredProc
dans la liste déroulante. A l’exécution, affectez à la propriété DataSet une
référence à TADOStoredProc.
ADOStoredProc1.Close;
DataSource1.DataSet := ADOStoredProc1;
ADOStoredProc1.Open;
Il est également possible d’accéder à l’ensemble de résultats et de le manipuler
en employant les propriétés et méthodes de déplacement et de modification
héritées de TDataSet. Pour des informations sur la modification des données de
composants ensemble de données, voir “Modification des données” à la
page 18-24. Pour des informations sur les déplacements dans les lignes des
composants ensembles de données, voir “Navigation dans les ensembles de
données” à la page 18-10.

Exécution de commandes
Les composants ADO proposés par Delphi permettent à une application
d’exécuter des commandes. Cette section décrit comment exécuter des
commandes et les composants à utiliser pour ce faire.
Dans l’environnement ADO, les commandes sont une représentation sous forme
de texte de demandes d’actions spécifiques à un fournisseur. Ce sont

23-28 Guide du développeur


Exécution de commandes

généralement des instructions SQL utilisant le langage de définition de données


(DDL) ou le langage de manipulation de données (DML). Le langage utilisé dans
ces commandes est spécifique au fournisseur, mais, généralement, il respecte le
standard SQL-92 du langage SQL.
Il est possible d’exécuter des commandes depuis plusieurs composants Delphi.
Chaque composant gérant les commandes exécute les commandes d’une manière
légèrement différente, avec divers avantages et inconvénients. Le composant à
utiliser pour une commande spécifique est déterminé par le type de commande
et le fait que la commande renvoie ou pas un ensemble de résultats. En général,
pour les commandes ne renvoyant pas d’ensemble de résultats, utilisez le
composant TADOCommand (même si le composant TADODataSet peut également
exécuter ces commandes). Pour les commandes renvoyant un ensemble de
résultats, exécutez la commande depuis TADODataSet ou utilisez l’instruction
SQL de la propriété SQL d’un TADOQuery.
Le composant TADOCommand permet d’exécuter des commandes, une
commande à la fois. Il est avant tout conçu pour l’exécution de commandes ne
renvoyant pas d’ensemble de résultats, comme les instructions SQL du langage
de définition de données (DDL). Cependant, il est capable via une version
redéfinie de sa méthode Execute de renvoyer un ensemble de résultats qui peut
être utilisé par un composant ensemble de données ADO.
Spécifiez la commande dans la propriété CommandText. Vous pouvez,
éventuellement, décrire la commande en utilisant la propriété CommandType. Si le
type spécifique n’est pas indiqué, c’est le serveur qui le détermine en se basant
sur la commande spécifiée par CommandText. Vous pouvez utiliser des
paramètres dans les commandes d’un composant commande ADO, des valeurs
étant substituées aux paramètres avant l’exécution de la commande. Pour
pouvoir exécuter une commande, le composant commande ADO doit disposer
d’une connexion établie avec un stockage de données ADO.

Spécification de la commande
Pour spécifier la commande exécutée par le composant commande ADO, utilisez
sa propriété CommandText. A la conception, utilisez l’inspecteur d’objet pour
entrer la commande (une instruction SQL, un nom de table ou de procédure)
dans la propriété CommandText. A l’exécution, affectez à la propriété
CommandText une valeur String contenant la commande.
Si vous le souhaitez, spécifiez explicitement le type de commande utilisé à l’aide
de la propriété CommandType. Le type CommandType propose les constantes
suivantes : cmdText (si la commande est une instruction SQL), cmdTable (si c’est
un nom de table) et cmdStoredProc (si la commande spécifie le nom d’une
procédure stockée). A la conception, sélectionnez la valeur appropriée dans la
liste proposée par l’inspecteur d’objet. A l’exécution, affectez une valeur de type
TCommandType à la propriété CommandType.
with ADOCommand1 do begin
CommandText := 'AddEmployee';
CommandType := cmdStoredProc;
...
end;

Utilisation des composants ADO 23-29


Exécution de commandes

Utilisation de la méthode Execute


Avant de pouvoir exécuter la commande d’un composant commande ADO, le
TADOCommand doit disposer d’une connexion avec un stockage de données.
Pour davantage d’informations, voir “Connexion à un stockage de données en
utilisant des composants ensemble de données ADO” à la page 23-14.
Pour exécuter la commande, appelez la méthode Execute du composant
commande ADO. Pour les commandes ne nécessitant pas de paramètre ou
d’option d’exécution, appelez la version simple redéfinie de Execute qui n’attend
aucun paramètre de méthode.
with ADOCommand1 do begin
CommandText := 'UpdateInventory';
CommandType := cmdStoredProc;
Execute;
end;
Pour davantage d’informations sur l’exécution de commandes renvoyant un
ensemble de résultats, voir “Utilisation des ensembles de résultats des
commandes” à la page 23-30.

Annulation de commandes
Après avoir commencé une tentative d’exécution d’une commande (avec la
méthode Execute d’un composant TADOCommand), il est possible d’abandonner
en appelant la méthode Cancel.
procedure TDataForm.ExecuteButtonClick(Sender: TObject);
begin
ADOCommand1.Execute;
end;
procedure TDataForm.CancelButtonClick(Sender: TObject);
begin
ADOCommand1.Cancel;
end;
La méthode Cancel n’a d’effet que si une commande en attente a été exécutée de
manière asynchrone (eoAsynchExecute dans le paramètre ExecuteOptions de la
méthode Execute). Une commande est dite en attente si la méthode Execute a été
appelée mais n’est pas encore achevée ou n’a pas dépassé le délai limite. Il y a
un dépassement de délai si une commande n’est pas terminée ou abandonnée
avant le nombre de secondes spécifié par la propriété CommandTimeout. Pour
définir un délai différent de la valeur par défaut (30 secondes), affectez la valeur
souhaitée à la propriété CommandTimeout avant d’appeler la méthode Execute.

Utilisation des ensembles de résultats des commandes


L’exécution d’une commande renvoyant un ensemble de résultats s’effectue de la
même manière que celle d’une commande qui ne le fait pas, si ce n’est qu’un
composant ensemble de données ADO préexistant doit représenter l’ensemble de
résultats. La méthode Execute de TADOCommand renvoie un objet ensemble
d’enregistrements ADO. Affectez la valeur renvoyée à la propriété RecordSet d’un
composant ensemble de données ADO, par exemple un TADODataSet.

23-30 Guide du développeur


Exécution de commandes

Dans l’exemple suivant, l’ensemble d’enregistrements ADO produit par la


méthode Execute d’un composant TADOCommand (ADOCommand1) est affecté à
la propriété Recordset d’un composant TADODataSet (ADODataSet1).
with ADOCommand1 do begin
CommandText := 'SELECT Company, State ' +
'FROM customer ' +
'WHERE State = :StateParam';
CommandType := cmdText;
Parameters.ParamByName('StateParam').Value := 'HI';
ADODataSet1.Recordset := Execute;
end;
Dès qu’une valeur est affectée à la propriété Recordset d’un composant ensemble
de données ADO, le composant ensemble de données est automatiquement
activé et les données sont accessibles. Utilisez les méthodes et propriétés du
composant ensemble de données pour accéder par code aux données. Pour
utiliser les données dans des contrôles orientés données, utilisez un composant
TDataSource comme intermédiaire entre l’ensemble de données ADO et les
contrôles orientés données.
Pour des informations sur l’exécution de commandes ne renvoyant pas
d’ensemble de résultats, voir “Exécution de commandes” à la page 23-28.

Gestion des paramètres de commande


Une commande avec paramètres s’exécute comme une commande sans
paramètre, si ce n’est que des valeurs doivent être attribuées aux paramètres
avant l’exécution de la commande.
Pour chaque paramètre de la commande, un objet TParameter est
automatiquement ajouté à la propriété Parameters du composant TADOCommand.
A la conception, utilisez l’éditeur de paramètres pour accéder aux paramètres.
Appelez cet éditeur en cliquant sur le bouton points de suspension de la
propriété Parameters dans l’inspecteur d’objet. A l’exécution, utilisez les
propriétés et méthodes de TParameter pour spécifier ou connaître la valeur de
chaque paramètre.
with ADOCommand1 do begin
CommandText := 'INSERT INTO Talley ' +
'(Counter) ' +
'VALUES (:NewValueParam)';
CommandType := cmdText;
Parameters.ParamByName('NewValueParam').Value := 57;
Execute
end;
Accédez à un objet TParameter de Parameters en utilisant un indice représentant
sa position relativement aux autres paramètres dans la commande. Vous pouvez
aussi accéder aux paramètres de TParameter par leur nom en utilisant la méthode
TParameters.ParamByName.

Utilisation des composants ADO 23-31


23-32 Guide du développeur
Chapitre

Création et utilisation d’un ensemble


Chapter 24
24
de données client
TClientDataSet est un composant ensemble de données conçu pour fonctionner
sans le support de connectivité du moteur de bases de données Borland (BDE) ni
des objets ADO (ActiveX Data Objects). A la place, il utilise MIDAS.DLL,
beaucoup moins volumineux et plus simple à installer et à configurer. Vous
n’utilisez pas de composant base de données ou connexion ADO avec les
ensembles de données client car il n’y a pas de connexion de base de données.
Les ensembles de données client offrent toutes les fonctionnalités d’accès aux
données d’édition, de navigation, de contrainte de données et de filtrage
introduites par TDataset. Toutefois, l’application qui utilise un ensemble de
données client doit disposer du mécanisme permettant à l’ensemble de données
client de lire les données et d’écrire les mises à jour. Les ensembles de données
client interviennent à cet effet de plusieurs manières :
• En lisant et en écrivant les données dans un fichier linéaire directement
accessible depuis un composant ensemble de données client. Il s’agit du
mécanisme utilisé par les applications de bases de données linéaire. Pour plus
d’informations sur l’utilisation d’un ensemble de données client dans les
applications linéaires, voir “Applications de base de données linéaires” à la
page 13-15.
• En lisant à partir d’un autre ensemble de données. Les ensembles de données
client offrent divers mécanismes de copie de données à partir d’autres
ensembles de données. Ces mécanismes sont décrits dans “Copie de données
d’un autre ensemble de données” à la page 24-14.
• En utilisant une interface IAppServer pour obtenir des données d’un serveur
d’applications et y envoyer des mises à jour. Ce mécanisme est utilisé par les
clients dans une application de base de données multiniveau. Pour plus
d’informations sur la construction d’applications de bases de données
multiniveaux, voir chapitre 14, “Création d’applications multiniveaux”.

Création et utilisation d’un ensemble de données client 24-1


Manipulation des données avec un ensemble de données client

Ces mécanismes peuvent être combinés dans une même application qui utilise le
modèle “briefcase”. Les utilisateurs prennent un cliché des données en les
enregistrant dans un fichier linéaire afin de pouvoir les manipuler “off-line”.
Ultérieurement, l’application client applique les modifications depuis la copie
locale des données vers le serveur d’applications. Ce dernier applique ensuite les
modifications dans la base de données réelle et renvoie les erreurs à l’ensemble
de données client afin qu’elles soient traitées. Pour plus d’informations sur la
construction d’une application avec le modèle “briefcase”, voir “Utilisation du
modèle “briefcase”” à la page 13-20.

Manipulation des données avec un ensemble de données client


Comme tout ensemble de données, les ensembles de données client vous
permettent de fournir les données des contrôles orientés données à l’aide d’un
composant source de données. Voir chapitre 26, “Utilisation de contrôles de
données”pour plus d’informations sur l’affichage des informations de bases de
données dans les contrôles orientés données.
Etant que TClientDataSet est un descendant de TDataSet, les ensembles de données
client héritent de la puissance et de la fonctionnalité des propriétés, des méthodes
et des événements définis pour tous les composants ensemble de données. Pour
une présentation complète du comportement générique des ensembles de
données, voir chapitre 18, “Présentation des ensembles de données”
Les ensembles de données client diffèrent des autres ensembles de données en ce
sens qu’ils stockent toutes leurs données en mémoire. C’est pourquoi leur prise
en charge des fonctions de base de données communes peut élargir leur champ
d’action et impliquer de nouvelles contraintes.

Navigation parmi les données des ensembles de données client


Si une application utilise des contrôles orientés données standard, un utilisateur
peut se déplacer parmi les enregistrements d’un ensemble de données client
comme dans n’importe quel ensemble de données. Il est aussi possible d’avoir
recours au code pour se déplacer parmi les enregistrements ; utilisez pour cela
les méthodes standard comme First, GotoKey, Last, Next et Prior. Pour plus
d’informations sur ces méthodes, voir “Navigation dans les ensembles de
données” à la page 18-10.
Les ensembles de données client peuvent aussi utiliser des signets pour marquer
des enregistrements et se déplacer parmi eux. Pour plus d’informations sur le
marquage d’enregistrements, voir “Marquage d’enregistrements” à la page 18-15.
A la différence de la plupart des ensembles de données, les ensembles de
données client peuvent aussi positionner le curseur sur un enregistrement
particulier de l’ensemble de données en utilisant la propriété RecNo.
Habituellement, les applications utilisent RecNo pour déterminer le numéro de
l’enregistrement en cours. Toutefois, les ensembles de données client peuvent
affecter à RecNo un numéro d’enregistrement particulier pour que cet
enregistrement devienne l’enregistrement en cours.

24-2 Guide du développeur


Manipulation des données avec un ensemble de données client

Limitation des enregistrements affichés


Pour restreindre l’accès aux données sur une base temporaire, les applications
peuvent aussi définir des filtres et des portées. Lorsqu’une portée ou un filtre est
appliqué, l’ensemble de données client n’affiche pas toutes les données placées
dans sa mémoire cache mais uniquement celles correspondant aux conditions de
filtre. Pour plus d’informations sur l’utilisation de filtres, voir “Affichage et
édition d’ensembles de données en utilisant des filtres” à la page 18-19. Pour
plus d’informations sur l’utilisation de portées, voir “Manipulation d’un sous-
ensemble de données” à la page 20-12.
Avec la plupart des ensembles de données, les chaînes de filtre sont analysées dans
des commandes SQL qui sont ensuite implémentées sur le serveur de bases de
données. C’est pourquoi le langage SQL du serveur limite le nombre d’opérations
utilisées dans les filtres. Les ensembles de données client implémentent leur propre
support d’utilisation des filtres, ce qui implique davantage d’opérations qu’avec les
autres ensembles de données. Par exemple, lorsque vous utilisez un ensemble de
données client, les expressions de filtre peuvent inclure des opérateurs de chaîne
qui renvoient des sous-chaînes, des opérateurs qui analysent les valeurs de date et
d’heure, etc. Les ensembles de données client permettent également d’appliquer
des filtres sur des champs BLOB ou des types de champs complexes tels que les
champs ADT et les champs tableau. Pour plus de détails, voir la documentation en
ligne relative à TClientSet.Filter.
Lors de l’application de portées ou de filtres, l’ensemble de données client stocke
tous ses enregistrements en mémoire. La portée ou le filtre détermine simplement les
enregistrements accessibles aux contrôles qui naviguent parmi les données
provenant de l’ensemble de données client ou qui les affichent. Dans les applications
multiniveaux, vous pouvez également limiter les données stockées dans l’ensemble
de données client à un sous-ensemble d’enregistrements en fournissant des
paramètres au serveur d’applications. Pour plus d’informations à ce sujet, voir
“Limitation des enregistrements avec des paramètres” à la page 24-19.

Représentation des relations maître / détail


Comme les tables, les ensembles de données client gèrent les fiches maître/
détail. Lorsque vous définissez une relation maître/détail, vous reliez deux
ensembles de données de sorte que tous les enregistrements de l’un (ensemble
de données détail) correspondent toujours à un enregistrement unique dans
l’autre (ensemble de données maître). Pour plus d’informations sur les fiches
maître/détail, voir “Création de fiches maître-détail” à la page 20-27.
De plus, vous pouvez définir des relations maître/détail dans les ensembles de
données client qui utilisent des tables imbriquées, selon l’un des deux procédés
suivants :
• En obtenant d’un composant fournisseur des enregistrements contenant des
détails imbriqués : lorsqu’un composant fournisseur représente la table maître
dans une relation maître/détail, il crée automatiquement un champ ensemble
de données imbriqué pour représenter les détails de chaque enregistrement.

Création et utilisation d’un ensemble de données client 24-3


Manipulation des données avec un ensemble de données client

• En définissant les détails imbriqués à l’aide de l’éditeur de champs : durant la


phase de conception, cliquez avec le bouton droit sur l’ensemble de données
client et choisissez Editeur de champs. Ajoutez un nouveau champ persistant
à votre ensemble de données client en cliquant avec le bouton droit et en
choisissant Ajouter des champs. Définissez votre nouveau champ avec le type
champ ensemble de données. Dans l’éditeur de champs, définissez la structure
de votre table détail.
Lorsque votre ensemble de données client contient des ensembles de données
détail imbriqués, TDBGrid permet d’afficher les détails imbriqués dans une
fenêtre indépendante. Vous pouvez aussi afficher et éditer ces ensembles de
données dans des contrôles orientés données en utilisant un ensemble de
données client séparé pour l’ensemble détail. A la conception, créez des champs
persistants pour les champs de votre ensemble de données client, notamment un
champ ensemble de données pour l’ensemble détail imbriqué.
Vous pouvez maintenant créer un ensemble de données client pour représenter
l’ensemble détail imbriqué. Affectez à la propriété DataSetField de cet ensemble de
données client détail le champ persistant DataSet de l’ensemble de données maître.
Dans les applications multiniveaux, l’utilisation des ensembles détail imbriqués
s’impose si vous souhaitez appliquer les mises à jour à partir de tables maître et
détail dans le serveur d’applications. Dans les applications de base de données
linéaire, l’utilisation des ensembles détail imbriqués vous permet d’enregistrer les
détails avec les enregistrements maître dans un fichier linéaire, ce qui vous évite
de charger deux ensembles de données séparément et de recréer les index pour
rétablir la relation maître/détail.
Remarque Pour utiliser des ensembles détail imbriqués, la propriété ObjectView de
l’ensemble de données client doit être True.

Définition de contraintes pour les valeurs des données


Les ensembles de données client permettent de définir des contraintes sur les
données. Vous avez toujours la possibilité de fournir des contraintes
personnalisées. Vous pouvez ainsi fournir vos propres limites applicatives
concernant les valeurs que les utilisateurs peuvent envoyer à un ensemble de
données client. Pour plus d’informations sur la fourniture de contraintes
personnalisées, voir “Ajout de contraintes personnalisées” à la page 24-23.
De plus, si vous utilisez un composant fournisseur pour communiquer avec un
serveur de bases de données distant, le fournisseur peut fournir des contraintes
serveur à l’ensemble de données client. Pour plus d’informations sur la
communication de contraintes sur des données par le serveur d’applications à un
ensemble de données client, voir “Gestion des contraintes depuis le serveur” à la
page 24-22.
Dans les applications multiniveaux, il se peut que vous souhaitiez désactiver
l’application des contraintes sur les données, notamment lorsque l’ensemble de
données client ne contient pas tous les enregistrements de l’ensemble de données
correspondant sur le serveur d’applications. Voir “Gestion des contraintes” à la
page 24-22 pour plus d’informations sur la procédure à suivre et sur son utilité.

24-4 Guide du développeur


Manipulation des données avec un ensemble de données client

Comment déclarer des données en lecture seulement


TDataSet offre la propriété CanModify grâce à laquelle les applications peuvent
déterminer si les données d’un ensemble de données peuvent être éditées. Les
applications ne peuvent pas modifier la propriété CanModify car, pour certains
descendants TDataSet, c’est la base de données sous-jacente, et non l’application,
qui détermine si les données peuvent être modifiées.
Toutefois, comme les ensembles de données client représentent des données en
mémoire, votre application peut toujours déterminer si les utilisateurs peuvent
éditer ces données. Pour empêcher les utilisateurs de modifier les données,
mettez la propriété ReadOnly de l’ensemble de données client à True. En mettant
ReadOnly à True, la propriété CanModify passe à False.
A la différence des autres ensembles de données, il n’est pas nécessaire de
fermer un ensemble de données client pour modifier son état. Une application
peut placer un ensemble de données client en lecture seulement sur une base
temporaire en modifiant simplement la valeur de la propriété ReadOnly.

Edition des données


Les ensembles de données client représentent leurs données sous la forme d’un
paquet de données en mémoire. Ce paquet est la valeur de la propriété Data Par
défaut, toutefois, les modifications ne sont pas stockées dans la propriété Data. Les
insertions, suppressions et modifications (faites par l’utilisateur ou programmées)
sont stockées dans un journal interne de modifications, représenté par la propriété
Delta. L’utilisation d’un journal des modifications a une double finalité :
• Lorsque vous utilisez un fournisseur, le journal de modifications est requis
pour l’application des mises à jour dans le serveur d’applications.
• Dans toute application, le journal de modifications constitue un outil élaboré
pour l’annulation de modifications.
La propriété LogChanges vous permet de désactiver temporairement le journal de
modifications. Lorsque LogChanges vaut True, les modifications sont enregistrées
dans le journal. Si LogChanges vaut False, les modifications sont directement
réalisées dans la propriétés Data. Vous pouvez désactiver le journal de
modifications dans les applications à niveau unique si vous n’avez pas besoin de
la fonctionnalité d’annulation.
Les modifications restent dans le journal de modifications jusqu’à ce qu’elles
soient supprimées par l’application. Les applications suppriment les
modifications lors des tâches suivantes :
• Annulation des modifications.
• Enregistrement des modifications.
Remarque L’enregistrement de l’ensemble de données client dans un fichier ne supprime
pas les modifications du journal. Lorsque vous rechargez l’ensemble de données,
les propriétés Data et Delta sont inchangées par rapport à ce qu’elles étaient lors
de l’enregistrement des données.

Création et utilisation d’un ensemble de données client 24-5


Manipulation des données avec un ensemble de données client

Annulation des modifications


Même si la version originale d’un enregistrement reste inchangée dans la
propriété Data, l’utilisateur voit la toute dernière version de l’enregistrement à
chaque fois qu’il modifie l’enregistrement, le laisse et le sélectionne à nouveau. Si
un utilisateur ou une application modifie plusieurs fois un enregistrement,
chaque version de l’enregistrement est stockée dans une entrée différente du
journal de modifications.
La possibilité de stocker chaque modification apportée à un enregistrement
permet de supporter les opérations d’annulation à plusieurs niveaux :
• Pour supprimer la dernière modification apportée à un enregistrement, appelez
UndoLastChange. Cette méthode accepte un paramètre booléen, FollowChange,
qui indique si le curseur doit être repositionné sur l’enregistrement restauré
(True) ou s’il doit être laissé sur l’enregistrement en cours (False). Si un
enregistrement a subi plusieurs modifications, chaque appel à UndoLastChange
annule un niveau de modification. UndoLastChange renvoie une valeur
booléenne indiquant si l’annulation a réussi ou échoué. En cas de réussite,
UndoLastChange renvoie False. Utilisez la propriété ChangeCount pour
déterminer si d’autres modifications doivent être annulées. ChangeCount
indique le nombre de modifications stockées dans le journal de modifications.
• Plutôt que de supprimer chaque niveau de modification l’un après l’autre,
vous pouvez tous les supprimer en une seule fois. Pour supprimer toutes les
modifications apportées à un enregistrement, sélectionnez-le et appelez
RevertRecord. Cette méthode supprime toutes les modifications apportées à
l’enregistrement en cours.
• A tout moment pendant les modifications, vous pouvez enregistrer l’état courant
du journal de modifications à l’aide de la propriété SavePoint. La lecture
SavePoint renvoie un marqueur à la position courante dans le journal de
modifications. Ultérieurement, si vous souhaitez annuler toutes les modifications
opérées depuis la lecture du point de sauvegarde, attribuez à SavePoint la valeur
lue. Votre application peut obtenir des valeurs pour plusieurs points de
sauvegarde. Toutefois, lorsque vous sauvegardez le journal de modifications au
niveau d’un point de sauvegarde, les valeurs de tous les points de sauvegarde
postérieurement lues par votre application ne sont plus valides.
• Toutes les modifications enregistrées dans le journal de modifications peuvent
être abandonnées en appelant CancelUpdates. Cette méthode efface le journal
des modifications et annule tous les changements apportés à l’ensemble des
enregistrements. CancelUpdates doit être employée avec précaution. Après avoir
appelé la méthode CancelUpdates, il est impossible de récupérer les
modifications.

Enregistrement des modifications


Les ensembles de données client utilisent différents mécanismes pour intégrer les
modifications à partir du journal de modifications, suivant qu’ils sont utilisés
dans une application autonome ou qu’ils représentent des données émanant d’un
serveur d’applications distant. Quel que soit le mécanisme utilisé, le journal de
modifications est automatiquement vidé lorsque les mises à jour ont été intégrées.

24-6 Guide du développeur


Manipulation des données avec un ensemble de données client

Les applications autonomes peuvent simplement fusionner les modifications dans


la mémoire cache locale représentée par la propriété Data. Elles ne sont pas
concernées par la résolution de modifications locales à partir de changements
réalisés par d’autres utilisateurs. Pour fusionner le journal de modifications dans
la propriété Data, appelez la méthode MergeChangeLog. “Fusion des modifications
dans les données” à la page 24-30 décrit ce processus.
Vous ne pouvez pas utiliser MergeChangeLog dans les applications multiniveaux.
Le serveur d’applications requiert les informations contenues dans le journal de
modifications pour répercuter les enregistrements mis à jour sur les données
stockées dans la base de données. A la place, appelez ApplyUpdates, qui envoie
les modifications au serveur d’applications et met à jour la propriété Data que
lorsque les modifications ont été répercutées avec succès dans la base de
données. Voir “Application des mises à jour” à la page 24-24 pour plus
d’informations sur ce processus.

Tri et indexation
L’utilisation d’index présente plusieurs avantages pour vos applications :
• Ils permettent aux ensembles de données client de localiser les données
rapidement.
• Ils permettent à votre application de définir des relations entre les ensembles
de données clients, telles que des tables de référence ou des fiches maître/
détail.
• Ils spécifient l’ordre dans lequel les enregistrements apparaissent.
Si un ensemble de données client est utilisé dans une application multiniveau, il
hérite d’un index par défaut et d’un ordre de tri basés sur les données qu’il
reçoit du serveur d’applications. L’index par défaut s’appelle DEFAULT_ORDER.
Il est possible d’utiliser ce classement, mais il est impossible de modifier ou de
supprimer l’index.
En plus de l’index par défaut, l’ensemble de données client gère un deuxième
index, appelé CHANGEINDEX, à partir des enregistrements stockés dans le
journal de modifications (propriété Delta). CHANGEINDEX classe tous les
enregistrements de l’ensemble de données tels qu’ils apparaîtraient si les
modifications de Delta étaient appliquées. CHANGEINDEX est basé sur l’ordre
qu’il a hérité de DEFAULT_ORDER. Comme pour DEFAULT_ORDER, il est
impossible de modifier ou de supprimer l’index CHANGEINDEX.
Vous pouvez utiliser d’autres index existants d’un ensemble de données ou créer
vos propres index. Les sections suivantes décrivent comment créer et utiliser des
index avec des ensembles de données client.

Ajout d’un nouvel index


Pour créer un nouvel index pour un ensemble de données client, appelez
AddIndex. AddIndex vous permet de spécifier les propriétés de l’index :
• Le nom de l’index. Il permet de permuter les index à l’exécution.

Création et utilisation d’un ensemble de données client 24-7


Manipulation des données avec un ensemble de données client

• Les champs qui composent l’index. L’index utilise ces champs pour trier les
enregistrements et localiser les enregistrements dont les champs indexés
présentent une valeur particulière.
• La façon dont l’index trie les enregistrements. Par défaut, les index imposent
un ordre de tri croissant (selon la configuration de la machine). Cet ordre de
tri par défaut tient compte de la casse. Vous pouvez spécifier des options
pour que la totalité de l’index fasse la différence entre les majuscules et les
minuscules ou pour trier par ordre décroissant. Vous pouvez aussi spécifier
une liste de champs à trier sans tenir compte de la casse et une autre liste de
champs à trier par ordre décroissant.
• Le niveau par défaut de regroupement pris en charge par l’index.
Astuce L’indexation et le tri peuvent s’effectuer sur des champs calculés dans des
ensembles de données client.
Les index créés sont triés par ordre alphabétique selon la configuration de votre
machine. Le paramètre ixDescending peut être ajouté dans la liste des options
d’index pour outrepasser la configuration par défaut.
Remarque Lorsque vous créez les index en même temps que l’ensemble de données client,
vous pouvez faire en sorte qu’ils trient certains champs par ordre croissant et
certains autres par ordre décroissant. Voir “Création d’un ensemble de données à
l’aide de définitions de champ et d’index” à la page 13-17 pour plus de détails.
Par défaut, le tri des champs chaîne tient compte de la différence majuscules/
minuscules. Le paramètre ixCaseInsensitive peut être ajouté dans la liste des
options d’index pour outrepasser ce comportement par défaut.
Remarque Lorsque vous créez les index en même temps que l’ensemble de données client,
vous pouvez faire en sorte que certains ne tiennent pas compte de la casse sur
certains champs et que d’autres en tiennent compte sur d’autres champs. Voir
“Création d’un ensemble de données à l’aide de définitions de champ et
d’index” à la page 13-17 pour plus de détails.
Attention Les index que vous ajoutez à l’aide de AddIndex ne sont pas enregistrés lorsque
vous enregistrez l’ensemble de données client dans un fichier.

Suppression et permutation d’index


Pour supprimer un index ayant été créé pour un ensemble de données client,
appelez DeleteIndex et spécifiez le nom de l’index. Les index DEFAULT_ORDER
et CHANGEINDEX ne peuvent pas être supprimés.
Lorsque plusieurs index sont disponibles, il est possible de changer d’index pour
un ensemble de données client en utilisant la propriété IndexName. Lors de la
phase de conception, les index disponibles peuvent être sélectionnés dans la liste
déroulante de la propriété IndexName dans l’inspecteur d’objets.

24-8 Guide du développeur


Manipulation des données avec un ensemble de données client

Utilisation des index pour regrouper les données


Lorsque vous utilisez un index dans votre ensemble de données client, il impose
automatiquement un ordre de tri sur les enregistrements. En raison de cet ordre
de tri, des enregistrements adjacents contiennent généralement les mêmes valeurs
dans les champs qui composent l’index. Par exemple, considérons la portion de
table de commandes suivante indexée sur les champs SalesRep et Customer :

SalesRep Customer OrderNo Amount


1 1 5 100
1 1 2 50
1 2 3 200
1 2 6 75
2 1 1 10
2 3 4 200

En raison de l’ordre de tri, les valeurs identiques dans la colonne SalesRep


apparaissent regroupées. A l’intérieur des champs relatifs au représentant
(SalesRep) 1, les valeurs identiques de la colonne des clients (Customer)
apparaissent regroupées. En d’autres termes, les données sont regroupées par
représentant (SalesRep) puis, dans le groupe SalesRep, par client (Customer). A
chaque regroupement est associé un niveau. Dans notre exemple, le groupe
SalesRep est le niveau 1 (car il n’est imbriqué dans aucun autre groupe) et le
groupe Customer est le niveau 2 (car il est imbriqué dans le groupe de niveau 1).
Le niveau de regroupement correspond à l’ordre des champs dans l’index.
Lorsque vous créez un index, vous pouvez spécifier son niveau de regroupement
(qui peut englober tous les champs de l’index).
Les ensembles de données client vous permettent de déterminer la position de
l’enregistrement en cours dans un niveau de regroupement donné. Cela permet à
votre application d’afficher les enregistrements différemment, suivant qu’ils se
trouvent en tête, au milieu ou à la fin d’un groupe. Par exemple, vous pouvez
afficher une valeur de champ que si elle figure dans le premier enregistrement
d’un groupe et éliminer ainsi les doublons. Le résultat est le suivant à partir de
la table précédente :

SalesRep Customer OrderNo Amount


1 1 5 100
2 50
2 3 200
6 75
2 1 1 10
3 4 200

Création et utilisation d’un ensemble de données client 24-9


Manipulation des données avec un ensemble de données client

Pour déterminer la position de l’enregistrement en cours dans un groupe, utilisez


la méthode GetGroupState. GetGroupState accepte une valeur entière représentant
le niveau du groupe et renvoie une valeur indiquant la position de
l’enregistrement en cours dans le groupe (première position, dernière position ou
aucune des deux positions).

Indexation à la volée
Au lieu de créer un index faisant partie de l’ensemble de données client, vous
pouvez créer à la volée un index temporaire en spécifiant les champs à utiliser
dans la propriété IndexFieldNames. Les noms de champs à utiliser doivent être
séparés par des points virgules. La position des champs dans la liste est
déterminante.
Remarque Quand l’indexation est effectuée à la volée, il n’est pas possible de spécifier un
index en ordre décroissant ou ne tenant pas compte des différences majuscule/
minuscule.
Attention Les index créés à la volée ne supportent pas le regroupement.

Représentation des valeurs calculées


Comme pour tout ensemble de données, vous pouvez ajouter des champs
calculés à votre ensemble de données client. Il s’agit de champs dont les valeurs
sont calculées dynamiquement, généralement en fonction des valeurs d’autres
champs du même enregistrement. Pour plus d’informations sur l’utilisation des
champs calculés, voir “Définition d’un champ calculé” à la page 19-10.
Toutefois, les ensembles de données client vous permettent de mieux définir à
quel moment les champs sont calculés par l’utilisation de champs calculés de
façon interne. Pour plus d’informations sur l’utilisation des champs calculés de
façon interne, voir ci-après “Utilisation de champs calculés de façon interne dans
les ensembles de données client”.
Vous pouvez aussi indiquer aux ensembles de données client de créer des
valeurs calculées qui résument les données de plusieurs enregistrements à l’aide
des agrégats maintenus. Pour plus d’informations sur les agrégats maintenus,
voir “Utilisation des agrégats maintenus” à la page 24-11.

Utilisation de champs calculés de façon interne dans les ensembles de


données client
Dans les autres ensembles de données, votre application doit déterminer la
valeur des champs calculés chaque fois que l’enregistrement change ou que
l’utilisateur modifie l’un des champs de l’enregistrement courant. Elle réalise cela
dans un gestionnaire d’événement OnCalcFields.

24-10 Guide du développeur


Manipulation des données avec un ensemble de données client

Bien que vous puissiez utiliser ce procédé dans les ensembles de données client,
ces derniers, en enregistrant les valeurs calculées dans leurs données, vous
permettent de réduire au minimum le nombre de fois où les champs calculés
doivent être recalculés. Lorsque les valeurs calculées sont enregistrées avec
l’ensemble de données client, elles doivent toujours être recalculées lorsque
l’utilisateur modifie l’enregistrement en cours mais votre application n’a pas
besoin de recalculer les valeurs chaque fois que l’enregistrement en cours change.
Pour enregistrer les valeurs calculées dans les données de l’ensemble de données
client, utilisez des champs calculés de façon interne à la place de champs calculés.
Les champs calculés de façon interne, comme les champs calculés, sont calculés
dans un gestionnaire d’événement OnCalcFields. Toutefois, vous pouvez optimiser
votre gestionnaire d’événement en vérifiant la propriété State de votre ensemble
de données client. Lorsque State vaut dsInternalCalc, vous devez recalculer les
champs calculés de façon interne. Lorsque State vaut dsCalcFields, il suffit de
recalculer les champs calculés ordinaires.
Pour utiliser des champs calculés de façon interne, vous devez définir les
champs devant être calculés de façon interne avant de créer l’ensemble de
données client. Si vous créez l’ensemble de données client à l’aide de champs
persistants, définissez les champs devant être calculés de façon interne en
sélectionnant InternalCalc dans l’éditeur de champs. Si vous créez l’ensemble de
données client à l’aide de définitions de champ, mettez la propriété
InternalCalcField de la définition de champ adéquate à True.
Remarque D’autres types d’ensembles de données utilisent des champs calculés de façon
interne. Toutefois, les valeurs de ces champs n’y sont pas calculées dans un
gestionnaire d’événement OnCalcFields mais automatiquement par le BDE ou le
serveur de bases de données distant.

Utilisation des agrégats maintenus


Les ensembles de données client permettent de résumer les données émanant de
différents groupes d’enregistrements. Comme ces résumés sont automatiquement
mis à jour au fur et à mesure que sont modifiées les données dans l’ensemble de
données, ces données de résumé sont appelées “agrégats maintenus”.
Dans leur forme la plus simple, les agrégats maintenus vous permettent
d’obtenir des informations telles que la somme de toutes les valeurs d’une
colonne de l’ensemble de données client. Ils sont suffisamment souples, toutefois,
pour supporter un large éventail de calculs résumé et déterminer des sous-totaux
englobant différents groupes d’enregistrements définis par un champ de l’index
supportant le regroupement.

Spécification d’agrégats
Pour spécifier que vous voulez opérer des calculs synthétiques à partir des
enregistrements d’un ensemble de données client, utilisez la propriété Aggregates.
Aggregates est un ensemble de spécifications d’agrégat (TAggregate). Vous pouvez
ajouter des spécifications d’agrégat à votre ensemble de données client à l’aide
de l’éditeur de collection lors de la conception ou de la méthode Add de

Création et utilisation d’un ensemble de données client 24-11


Manipulation des données avec un ensemble de données client

Aggregates à l’exécution. Si vous souhaitez créer des composants champ pour les
agrégats, créez des champs persistants pour les valeurs synthétisées dans
l’éditeur de champs.
Remarque Lorsque vous créez des champs synthétisés, les objets agrégat appropriés sont
automatiquement ajoutés à la propriété Aggregates de l’ensemble de données
client. Ne les ajoutez-pas de façon explicite lors de la création des champs
persistants synthétisés. Pour plus de détails sur la création des champs
persistants synthétisés, voir “Définition d’un champ agrégat” à la page 19-13.
Pour chaque agrégat, la propriété Expression indique le calcul synthétique qu’elle
représente. Expression peut contenir une simple expression synthétique telle que
Sum(Field1)
ou une expression complexe qui combine les informations de plusieurs champs,
telle que
Sum(Qty * Price) - Sum(AmountPaid)
Les expressions d’agrégat incluent un ou plusieurs opérateurs de synthèse du
tableau 24.1

Tableau 24.1 Opérateurs de synthèse des agrégats maintenus


Opérateur Utilisation
Sum Somme des valeurs d’un champ numérique ou d’une expression
Avg Valeur moyenne d’un champ numérique ou date/heure ou d’une expression
Count Spécification du nombre de valeurs exprimées pour un champ ou pour une
expression
Min Valeur minimale d’un champ chaîne, numérique ou date/heure ou d’une expression
Max Valeur maximale d’un champ chaîne, numérique ou date/heure ou d’une expression

Les opérateurs de synthèse portent sur des valeurs de champ ou sur des
expressions conçues à partir de valeurs de champ à l’aide des mêmes opérateurs
que ceux utilisés pour la création de filtres. Vous ne pouvez pas, toutefois,
imbriquer des opérateurs de synthèse. Vous pouvez créer des expressions avec
des opérateurs à partir de valeurs synthétisées ou de valeurs synthétisées et de
constantes. Toutefois, vous ne pouvez pas combiner des valeurs synthétisées et
des valeurs de champ, car de telles expressions sont ambiguës (rien n’indique
quel enregistrement doit fournir la valeur de champ.) Ces règles sont illustrées
dans les expressions suivantes :

Sum(Qty * Price) {autorisé -- synthèse d’une expression portant sur des


champs}
Max(Field1) - Max(Field2) {autorisé -- expression à partir de synthèses}
Avg(DiscountRate) * 100 {autorisé -- expression à partir d’une synthèse et d’une
constante}
Min(Sum(Field1)) {non autorisé -- synthèses imbriquées}
Count(Field1) - Field2 {non autorisé -- expression à partir d’une synthèse et
d’un champ}

24-12 Guide du développeur


Manipulation des données avec un ensemble de données client

Agrégats de groupes d’enregistrements


Par défaut, les agrégats maintenus sont calculés afin qu’ils synthétisent tous les
enregistrements d’un ensemble de données client. Toutefois, vous pouvez
spécifier que l’opération ne porte que sur les enregistrements d’un groupe. Cela
vous permet d’obtenir des synthèses intermédiaires, comme des sous-totaux
impliquant des groupes d’enregistrements ayant une valeur de champ commune.
Pour spécifier un agrégat maintenu sur un groupe d’enregistrements, vous devez
disposer d’un index à partir duquel peut s’opérer le regroupement. Voir
“Utilisation des index pour regrouper les données” à la page 24-9, pour plus
d’informations sur le regroupement.
Une fois que vous disposez d’un index qui regroupe les données en fonction de
la synthèse que vous voulez opérer, spécifiez les propriétés IndexName et
GroupingLevel d’agrégat pour indiquer l’index à utiliser et le groupe ou sous-
groupe de cet index qui définit les enregistrements à synthétiser.
Par exemple, considérons la portion de table de commandes suivante triée par
représentants (SalesRep) puis par clients (Customer) :

SalesRep Customer OrderNo Amount


1 1 5 100
1 1 2 50
1 2 3 200
1 2 6 75
2 1 1 10
2 3 4 200

Le code suivant définit un agrégat maintenu qui indique le montant total des
ventes réalisé par chaque représentant :
Agg.Expression := 'Sum(Amount)';
Agg.IndexName := 'SalesCust';
Agg.GroupingLevel := 1;
Agg.AggregateName := 'Total Rep';
Pour ajouter un agrégat qui synthétise chaque client pour un représentant donné,
créez un agrégat maintenu de niveau 2.
Les agrégats maintenus qui synthétisent un groupe d’enregistrements sont
associés à un index spécifique. La propriété Aggregates peut inclure des agrégats
qui utilisent différents index. Toutefois, seuls les agrégats qui synthétisent la
totalité de l’ensemble de données client et ceux qui utilisent l’index en cours sont
valides. La modification de l’index en cours détermine les agrégats qui sont
valides. Pour déterminer les agrégats valides à un moment donné, utilisez la
propriété ActiveAggs.

Obtention de valeurs d’agrégat


Pour obtenir la valeur d’un agrégat maintenu, appelez la méthode Value de l’objet
TAggregate qui représente l’agrégat. Value renvoie l’agrégat maintenu du groupe
qui contient l’enregistrement en cours de l’ensemble de données client.

Création et utilisation d’un ensemble de données client 24-13


Copie de données d’un autre ensemble de données

Lorsque la synthèse porte sur la totalité de l’ensemble de données client, vous


pouvez appeler Value à tout moment pour obtenir l’agrégat maintenu. Toutefois,
lorsque la synthèse porte sur des informations regroupées, vous devez veiller à
ce que l’enregistrement en cours se trouve dans le groupe à synthétiser. Aussi
est-il judicieux d’obtenir les valeurs d’agrégat à des moments précis, comme
lorsque vous vous positionnez sur le premier ou le dernier enregistrement d’un
groupe. Utilisez la méthode GetGroupState pour déterminer la position de
l’enregistrement en cours dans un groupe.
Pour afficher les agrégats maintenus dans les contrôles orientés données, utilisez
l’éditeur de champs pour créer un composant champ agrégat persistant. Lorsque
vous spécifiez un champ agrégat dans l’éditeur de champs, les agrégats de
l’ensemble de données client sont automatiquement mis à jour pour qu’ils
intègrent la spécification d’agrégat appropriée. La propriété AggFields contient le
nouveau composant champ agrégat tandis que la méthode FindField le renvoie.

Ajout d’informations d’application aux données


Les développeurs d’applications peuvent ajouter des informations personnalisées
à la propriété Data de l’ensemble de données client. Comme ces informations
sont regroupées dans le paquet de données, elles sont incluses lorsque vous
enregistrez les données dans un fichier ou un flux. Elles sont copiées lorsque
vous copiez les données dans un autre ensemble de données. Elles peuvent aussi
être intégrées dans la propriété Delta afin que le serveur d’applications puisse les
lire lorsqu’il reçoit les mises à jour de l’ensemble de données client.
Pour enregistrer les informations d’application dans la propriété Data, utilisez la
méthode SetOptionalParam. Cette méthode vous permet de stocker un OleVariant
qui contient les données sous un nom spécifique.
Pour extraire ces informations d’application, utilisez la méthode
GetOptionalParam, en transmettant le nom utilisé pour leur stockage.

Copie de données d’un autre ensemble de données


Pour copier les données d’un autre ensemble de données lors de la conception,
cliquez avec le bouton droit sur l’ensemble de données et choisissez Affecter
données locales. Une boîte de dialogue apparaît, affichant tous les ensembles de
données disponibles dans votre projet. Sélectionnez celui que vous souhaitez
copier et choisissez OK. Lorsque vous copiez l’ensemble de données source,
votre ensemble de données client est automatiquement activé.
Pour copier à partir d’un autre ensemble de données à l’exécution, vous pouvez
affecter ses données directement ou, si la source est un autre ensemble de
données client, cloner le curseur.

24-14 Guide du développeur


Copie de données d’un autre ensemble de données

Affectation directe des données


Vous pouvez utiliser la propriété Data de l’ensemble de données client pour
affecter des données à un ensemble de données client depuis un autre ensemble
de données. Data est un OleVariant qui se présente sous la forme d’un paquet de
données. Un paquet de données peut émaner d’un autre ensemble de données
client ou de tout autre ensemble de données avec l’aide d’un fournisseur. Une
fois qu’un paquet de données est affecté à Data, son contenu est
automatiquement affiché dans les contrôles orientés données connectés à
l’ensemble de données client par un composant source de données.
Lorsque vous ouvrez un ensemble de données client qui utilise un composant
fournisseur, les paquets de données sont automatiquement affectés à Data. Voir
“Utilisation d’un ensemble de données client avec un fournisseur de données” à
la page 24-16 pour plus d’informations sur l’utilisation de fournisseurs avec les
ensembles de données client.
Lorsque votre ensemble de données client n’utilise pas de fournisseur, vous
pouvez copier les données à partir d’un autre ensemble de données client
comme suit :
ClientDataSet1.Data := ClientDataSet2.Data;
Remarque Lorsque vous copiez la propriété Data d’un autre ensemble de données client,
vous copiez également le journal de modifications, mais la copie ne reflète aucun
filtre ni aucune portée appliqués. Pour inclure les filtres ou les portées, vous
devez cloner le curseur de l’ensemble de données source.
Si vous copiez à partir d’un ensemble de données autre qu’un ensemble de
données client, vous pouvez créer un composant fournisseur d’ensembles de
données, le relier à l’ensemble de données source puis copier les données :
TempProvider := TDataSetProvider.Create(Form1);
TempProvider.DataSet := SourceDataSet;
ClientDataSet1.Data := TempProvider.Data;
TempProvider.Free;
Remarque Lorsque vous affectez directement les données à la propriété Data, le nouveau
paquet de données n’est pas fusionné dans les données existantes. Au lieu de
cela, toutes les anciennes données sont remplacées.
Si vous souhaitez fusionner les modifications à partir d’un autre ensemble de
données plutôt que de copier ses données, vous devez utiliser un composant
fournisseur. Créez un fournisseur d’ensembles de données comme dans
l’exemple précédent, mais attachez-le à l’ensemble de données de destination et
au lieu de copier la propriété Data, utilisez la méthode ApplyUpdates :
TempProvider := TDataSetProvider.Create(Form1);
TempProvider.DataSet := ClientDataSet1;
TempProvider.ApplyUpdates(SourceDataSet.Delta, -1, ErrCount);
TempProvider.Free;

Création et utilisation d’un ensemble de données client 24-15


Utilisation d’un ensemble de données client avec un fournisseur de données

Clonage d’un curseur d’ensemble de données client


TClientDataSet dispose d’une procédure CloneCursor qui vous permet de travailler
avec une autre vue d’un ensemble de données client à l’exécution. CloneCursor
permet à un deuxième ensemble de données client de partager les données de
l’ensemble de données client original. Ceci est moins onéreux que de copier
toutes les données originales mais, comme les données sont partagées, le second
ensemble de données client ne peut pas modifier les données sans affecter
l’ensemble de données client original.
CloneCursor prend trois paramètres : Source spécifie l’ensemble de données client
à cloner. Les deux autres paramètres (Reset et KeepSettings) indiquent si d’autres
informations que les données doivent être copiées. Il peut s’agir de tout filtre, de
l’index en cours, des liens vers une table maître (lorsque l’ensemble de données
source est un ensemble détail), de la propriété ReadOnly et de tout lien vers un
composant connexion ou une interface fournisseur.
Lorsque Reset et KeepSettings valent False, un ensemble de données client cloné
est ouvert et les paramètres de l’ensemble de données client source sont utilisés
pour définir les propriétés de l’ensemble de données destination. Lorsque Reset
vaut True, les propriétés de l’ensemble de données client destination reçoivent les
valeurs par défaut (aucun index ni filtre, aucune table maître, ReadOnly vaut
False et aucun composant connexion ou fournisseur n’est spécifié). Lorsque
KeepSettings vaut True, les propriétés de l’ensemble de données destination ne
sont pas modifiées.

Utilisation d’un ensemble de données client avec un fournisseur


de données
Lorsque vous utilisez un ensemble de données client dans une application
multiniveau, l’ensemble de données client obtient les données depuis un
fournisseur du serveur d’applications et, après les avoir modifiées localement,
applique les mises à jour dans la base de données distante. Il est également
possible d’utiliser un ensemble de données client avec un fournisseur résidant
dans la même application.
Les étapes suivantes décrivent la façon d’utiliser un ensemble de données client
avec un fournisseur :
1 Spécification d’un fournisseur de données.
2 Eventuellement, Obtention de paramètres du serveur d’applications ou
Transmission de paramètres au serveur d’applications.
3 Eventuellement, Redéfinition de l’ensemble de données sur le serveur
d’applications.
4 Extraction des données depuis un serveur d’applications.
5 Gestion des contraintes reçues du serveur d’applications.

24-16 Guide du développeur


Utilisation d’un ensemble de données client avec un fournisseur de données

6 Mise à jour des enregistrements.


7 Rafraîchissement des enregistrements.
En outre, les ensembles de données client vous permettent de communiquer avec
le fournisseur en utilisant un événement personnalisé.

Spécification d’un fournisseur de données


Pour qu’un ensemble de données client puisse recevoir des données d’un serveur
d’applications et lui renvoyer les mises à jour, il doit au préalable être associé à
un fournisseur d’ensembles de données. Pour associer un ensemble de données
client à un fournisseur dans une application multiniveau, utilisez les propriétés
RemoteServer et ProviderName. Dans les applications à niveau unique et dans les
applications client utilisées dans un modèle “briefcase” comme applications à
niveau unique temporaires, ces propriétés ne sont pas utilisées. Quand vous
utilisez un ensemble de données client avec un fournisseur instancié dans la
même application, vous n’avez pas besoin d’utiliser la propriété RemoteServer,
mais vous pouvez quand même utiliser la propriété ProviderName, à condition
que le composant fournisseur aie le même Owner que l’ensemble de données
client.
RemoteServer spécifie le nom d’un composant connexion à partir duquel vous
pouvez obtenir une liste de fournisseurs. Le composant connexion se trouve dans
le même module de données que l’ensemble de données client. Il établit et
maintient la connexion au serveur d’applications, c’est pourquoi il est parfois
appelé “l’agent ou le broker des données”. Pour plus d’informations, voir
“Structure de l’application client” à la page 14-4.
Après avoir spécifié en mode conception la propriété RemoteServer, vous pouvez
sélectionner un fournisseur dans la liste déroulante de la propriété ProviderName
dans l’inspecteur d’objets. Cette liste contient également tous les fournisseurs
locaux appartenant au même module de données. A l’exécution, vous pouvez
passer d’un fournisseur à l’autre en modifiant la propriété ProviderName.
Pour utiliser un fournisseur local ayant un différent Owner, vous devez créer
l’association pendant l’exécution en utilisant la méthode SetProvider de l’ensemble
de données client.

Obtention de paramètres du serveur d’applications


Il existe deux cas dans lesquels l’application client doit obtenir des valeurs de
paramètre du serveur d’applications :
• Le client doit connaître la valeur de paramètres de sortie sur une procédure
stockée.
• Le client souhaite initialiser les paramètres de sortie d’une requête ou d’une
procédure stockée d’après les valeurs courantes d’un composant requête ou
procédure stockée sur le serveur d’applications.

Création et utilisation d’un ensemble de données client 24-17


Utilisation d’un ensemble de données client avec un fournisseur de données

Un ensemble de données client stocke les valeurs des paramètres dans sa


propriété Params. Ces valeurs sont rafraîchies par des paramètres de sortie
quelconques chaque fois que l’ensemble de données client lit les données depuis
le serveur d’applications. Il existe cependant des cas où une application client a
besoin de paramètres de sortie alors qu’elle ne lit pas de données.
Pour lire des paramètres de sortie sans lire d’enregistrement, ou pour initialiser
des paramètres d’entrée, l’ensemble de données client peut extraire des valeurs
de paramètre du serveur d’applications en appelant la méthode FetchParams. Les
paramètres sont renvoyés dans un paquet de données depuis le serveur
d’applications et affectés à la propriété Params de l’ensemble de données client.
A la conception, la propriété Params peut être initialisée en cliquant avec le
bouton droit sur l’ensemble de données client et en choisissant Analyser Params.
Remarque La méthode FetchParams (ou la commande Analyser Params) ne fonctionne que si
l’ensemble de données client est connecté à un fournisseur dont l’ensemble de
données associé peut fournir des paramètres. TDataSetProvider peut fournir des
valeurs de paramètre s’il représente un composant requête ou procédure stockée.
Si vous travaillez avec un serveur d’applications sans état, vous ne pouvez pas
utiliser FetchParams pour extraire des paramètres de sortie. Dans un serveur
d’applications sans état, d’autres clients peuvent modifier et ré-exécuter la
requête ou la procédure stockée, en changeant les paramètres de sortie avant
l’appel de FetchParams. Pour extraire des paramètres de sortie depuis un serveur
d’applications sans état, utilisez la méthode Execute. Si le fournisseur est associé
à une requête ou à une procédure stockée, Execute indique au fournisseur
d’exécuter la requête ou la procédure stockée et de renvoyer les éventuels
paramètres de sortie. Ces paramètres renvoyés sont alors utilisés pour mettre
automatiquement à jour la propriété Params.

Transmission de paramètres au serveur d’applications


Les ensembles de données client peuvent transmettre des paramètres au serveur
d’applications pour spécifier les données qui doivent figurer dans les paquets de
données qu’il envoie. Ces paramètres peuvent spécifier :
• Les valeurs de paramètre d’une requête ou d’une procédure stockée exécutée
sur le serveur d’applications
• Les valeurs de champ qui limitent le nombre d’enregistrements envoyés dans
les paquets de données
Vous pouvez spécifier les valeurs de paramètre que votre ensemble de données
client envoie au serveur d’applications lors de la conception ou à l’exécution.
Lors de la conception, sélectionnez l’ensemble de données client et double-
cliquez sur la propriété Params dans l’inspecteur d’objets. Cela appelle l’éditeur
de collection, dans lequel vous pouvez ajouter, supprimer ou réorganiser les
paramètres. Lorsque vous sélectionnez un paramètre dans l’éditeur de collection,
vous pouvez en modifier les propriétés à l’aide de l’inspecteur d’objets.

24-18 Guide du développeur


Utilisation d’un ensemble de données client avec un fournisseur de données

A l’exécution, utilisez la méthode CreateParam de la propriété Params pour


ajouter des paramètres à votre ensemble de données client. CreateParam renvoie
un objet paramètre, doté d’un nom, d’un type de paramètre et d’un type de
données particuliers. Vous pouvez alors utiliser les propriétés de cet objet
paramètre pour affecter une valeur au paramètre.
Par exemple, le code suivant attribue la valeur 605 à un paramètre appelé CustNo :
with ClientDataSet1.Params.CreateParam(ftInteger, 'CustNo', ptInput) do
AsInteger := 605;
Si l’ensemble de données client n’est pas actif, vous pouvez envoyer les
paramètres au serveur d’applications et récupérer un paquet de données qui
reflète ces valeurs de paramètre en mettant simplement la propriété Active à True.

Envoi de paramètres de requête ou de procédure stockée


Lorsque le fournisseur sur le serveur d’applications représente les résultats d’une
requête ou d’une procédure stockée, vous pouvez utiliser la propriété Params
pour spécifier les valeurs des paramètres. Quand l’ensemble de données client
extrait les données depuis un serveur d’applications ou utilise sa méthode Execute
pour lancer une requête ou une procédure stockée ne renvoyant pas d’ensemble
de données, il transmet les valeurs de ces paramètres avec l’extraction des
données ou la commande execute. Lorsque le fournisseur reçoit ces paramètres, il
les affecte à la requête ou à la procédure stockée qui lui est associée. Le serveur
d’applications exécute alors la requête ou la procédure stockée en utilisant les
valeurs de ces paramètres et, si l’ensemble de données client extrait les données,
il commence à fournir les données, à partir du premier enregistrement de
l’ensemble de résultats.
Remarque Les noms de paramètre doivent correspondre aux noms des paramètres
correspondants du composant requête ou procédure stockée dans le serveur
d’applications.

Limitation des enregistrements avec des paramètres


Lorsque le fournisseur sur le serveur d’applications représente les résultats d’un
composant table, vous pouvez utiliser la propriété Params pour limiter le nombre
d’enregistrements fournis à la propriété Data.
Chaque nom de paramètre doit correspondre au nom d’un champ dans le
composant TTable sur le serveur d’applications. Le composant fournisseur sur le
serveur d’applications n’envoie que les enregistrements dont les valeurs dans les
champs identiques correspondent à celles attribuées aux paramètres.
Par exemple, supposons une application client qui affiche les commandes d’un
seul client. Lorsque l’utilisateur identifie le client, l’ensemble de données client
définit sa propriété Params pour inclure un paramètre unique nommé CustID,
dont la valeur identifie le client dont les commandes seront affichées. Quand
l’ensemble de données client extrait les données depuis le serveur d’applications,
il transmet la valeur de ce paramètre. Le serveur d’applications n’envoie ensuite
que les enregistrements concernant le client identifié. Ceci est beaucoup plus
efficace que la solution dans laquelle tous les enregistrements de commandes sont
envoyés par le serveur d’applications à l’application client puis filtrés côté client.

Création et utilisation d’un ensemble de données client 24-19


Utilisation d’un ensemble de données client avec un fournisseur de données

Redéfinition de l’ensemble de données sur le serveur d’applications


Habituellement, le fournisseur sur le serveur d’applications est associé à un
ensemble de données qui détermine les données fournies aux clients. Cet
ensemble de données peut posséder une propriété spécifiant une instruction SQL
pour générer les données, ou bien, il peut représenter une table particulière
d’une base de données ou une procédure stockée.
Si le fournisseur le permet, l’ensemble de données client peut redéfinir la
propriété qui indique les données représentées par l’ensemble de données. Pour
ce faire, définissez la propriété CommandText de l’ensemble de données client.
CommandText contient une instruction SQL qui remplace celle de l’ensemble de
données du fournisseur, ou bien, il contient le nom d’une table ou d’une procédure
stockée qui remplace la table ou la procédure stockée que l’ensemble de données
représente. Cela permet à l’ensemble de données client de spécifier de façon
dynamique les données qu’il veut voir.
Par défaut, les fournisseurs n’autorisent pas les ensembles de données client à
spécifier de cette façon une valeur de CommandText. Pour permettre à l’ensemble
de données client d’utiliser sa propriété CommandText, vous devez ajouter
poAllowCommandText à la propriété Options du fournisseur sur le serveur
d’applications. Sinon, la valeur de CommandText est ignorée.
L’ensemble de données client envoie sa chaîne CommandText au fournisseur à
deux moments :
• Quand l’ensemble de données client est ouvert pour la première fois. Une fois
que le premier paquet de données est extrait du serveur d’applications,
l’ensemble de données client n’envoie plus CommandText lorsqu’il lit les
paquets de données suivants.
• Quand l’ensemble de données client envoie une commande Execute au serveur
d’applications.
Pour envoyer une commande SQL ou pour changer le nom d’une table ou d’une
procédure stockée à un autre moment, vous devez utiliser explicitement
l’interface IAppServer par le biais de la propriété AppServer.

Extraction des données depuis un serveur d’applications


Le tableau suivant indique les propriétés et les méthodes du composant
TClientDataSet qui permettent de déterminer comment les données sont extraites
d’un serveur d’applications dans une application multiniveau :
Tableau 24.2 Propriétés et méthodes des ensembles de données client pour la gestion des extractions
de données
Propriété ou méthode Utilisation
FetchOnDemand Détermine si un ensemble de données client extrait
(propriété) automatiquement les données au fur et à mesure des besoins,
ou s’il compte sur l’application pour appeler les fonctions
GetNextPacket, FetchBlobs et FetchDetails de l’ensemble de
données client afin d’extraire des données supplémentaires.

24-20 Guide du développeur


Utilisation d’un ensemble de données client avec un fournisseur de données

Tableau 24.2 Propriétés et méthodes des ensembles de données client pour la gestion des extractions
de données (suite)
Propriété ou méthode Utilisation
PacketRecords (propriété) Spécifie le type ou le nombre d’enregistrements à renvoyer dans
chaque paquet de données.
GetNextPacket (méthode) Extrait le paquet de données suivant du serveur d’applications.
FetchBlobs (méthode) Extrait tous les champs BLOB de l’enregistrement en cours
lorsque le serveur d’applications n’inclut pas de données BLOB
automatiquement.
FetchDetails (méthode) Extrait les ensembles de données détail imbriqués de
l’enregistrement en cours lorsque le serveur d’applications ne
les inclut pas automatiquement dans des paquets de données.

Par défaut, un ensemble de données client extrait tous les enregistrements du


serveur d’applications. La façon dont les données sont extraites peut être définie
en utilisant PacketRecords et FetchOnDemand.
PacketRecords spécifie la quantité d’enregistrements à extraire à la fois et le type
des enregistrements à renvoyer. Par défaut, PacketRecords vaut -1, ce qui signifie
que tous les enregistrements disponibles sont extraits à la fois, que ce soit à la
première ouverture de l’application ou lorsqu’elle appelle explicitement
GetNextPacket. Lorsque PacketRecords vaut -1, l’ensemble de données client
n’extrait pas de données supplémentaires après la première extraction des
données, car il dispose de tous les enregistrements disponibles.
Pour extraire les enregistrements par petits lots, affectez à PacketRecords une valeur
correspondant au nombre d’enregistrements voulu. Par exemple, l’instruction
suivante fixe la taille de chaque paquet de données à dix enregistrements :
ClientDataSet1.PacketRecords := 10;
Ce processus d’extraction d’enregistrements par petits lots est appelé “extraction
incrémentale”. Les ensembles de données client utilisent l’extraction incrémentale
lorsque PacketRecords est supérieur à zéro. Par défaut, l’ensemble de données
client appelle GetNextPacket pour extraire les données en fonction des besoins.
Les paquets extraits sont ajoutés à la fin des données se trouvant déjà dans
l’ensemble de données client.
GetNextPacket renvoie le nombre d’enregistrements extraits. Si la valeur renvoyée
est équivalente à la valeur de PacketRecords, c’est que tous les enregistrements
disponibles n’ont pas été traités. Si la valeur renvoyée est supérieure à 0 mais
inférieure à PacketRecords, c’est que le dernier enregistrement a été atteint durant
l’opération d’extraction. Si GetNextPacket renvoie 0, c’est qu’il n’y a plus aucun
enregistrement à extraire.
Attention L’extraction incrémentale ne fonctionne que si le module de données distant
préserve les informations d’état. C’est-à-dire que vous ne devez pas utiliser MTS et
que le module de données distant doit être configuré de telle sorte que chaque
application client possède sa propre instance du module de données. Voir “Gestion
des informations d’état dans les modules de données distants” à la page 14-30 pour
plus d’informations sur l’utilisation de l’extraction incrémentale dans les modules de
données distants sans état.

Création et utilisation d’un ensemble de données client 24-21


Utilisation d’un ensemble de données client avec un fournisseur de données

La propriété PacketRecords peut aussi être utilisée pour extraire des informations
métadonnées sur une base de données du serveur d’applications. Pour extraire
des informations métadonnées, PacketRecords doit valoir 0.
L’extraction automatique d’enregistrements est contrôlée par la propriété
FetchOnDemand. Lorsque FetchOnDemand vaut True (la valeur par défaut),
l’extraction automatique est activée. Pour éviter que l’extraction automatique des
enregistrements se produise, mettez FetchOnDemand à False. Si FetchOnDemand est
à False, l’application doit appeler explicitement GetNextPacket pour extraire des
enregistrements.
Les applications qui doivent représenter des ensembles de données très
volumineux accessibles en lecture seulement peuvent désactiver FetchOnDemand
afin que les ensembles de données client n’essaient pas de charger plus de
données que la mémoire ne peut en contenir. Entre les extractions, l’ensemble de
données client libère sa mémoire cache à l’aide de la méthode EmptyDataSet.
Cette approche, toutefois, ne fonctionne pas très bien lorsque le client doit
renvoyer les mises à jour au serveur d’applications.

Gestion des contraintes


Les ensembles de données client gèrent deux types de contraintes. Ce sont
• les contraintes envoyées depuis le serveur d’applications dans des paquets de
données.
• les contraintes personnalisées fournies par l’application client.

Gestion des contraintes depuis le serveur


Par défaut, les contraintes serveur et les expressions par défaut sont transmises par le
serveur d’applications à des ensembles de données client, où elles peuvent être
appliquées aux saisies de données de l’utilisateur. Quand les contraintes sont en
vigueur, les modifications de données effectuées par l’utilisateur dans une
application client qui violeraient des contraintes sont contrôlées au niveau du client
et ne sont jamais transmises au serveur d’applications pour être rejetées en fin de
compte par le serveur de base de données. Cela signifie que les mises à jour générant
des erreurs lors du processus de mise à jour sont moins nombreuses.
Bien que l’importation de contraintes et d’expressions serveur soit extrêmement
intéressante pour permettre à un développeur de préserver l’intégrité des
données entre plates-formes et applications, une application peut avoir besoin de
désactiver temporairement des contraintes. Par exemple, si une contrainte serveur
est basée sur la valeur maximum en cours dans un champ mais que l’ensemble
de données client extrait plusieurs paquets d’enregistrements, la valeur
maximum en cours dans un champ sur le client peut être différente de la valeur
maximum sur le serveur de base de données et les contraintes peuvent être
appelées différemment. Dans un autre cas, si une application client applique un
filtre aux enregistrements quand des contraintes sont activées, ce filtre peut
provoquer des interférences indésirables avec les conditions des contraintes. Dans
chacun de ces cas, une application peut désactiver le contrôle des contraintes.

24-22 Guide du développeur


Utilisation d’un ensemble de données client avec un fournisseur de données

Pour désactiver temporairement les contraintes, appelez la méthode


DisableConstraints d’un ensemble de données client. A chaque appel de
DisableConstraints, un compteur de références est incrémenté. Tant que la valeur
de ce compteur est supérieure à zéro, les contraintes ne sont pas en vigueur sur
l’ensemble de données client.
Pour réactiver les contraintes pour l’ensemble de données client, appelez la
méthode EnableConstraints de l’ensemble de données. Chaque appel à
EnableConstraints décrémente le compteur de références. Quand ce compteur
atteint la valeur zéro, les contraintes sont à nouveau activées.
Conseil Appelez toujours DisableConstraints et EnableConstraints dans des blocs appariés
pour garantir que les contraintes sont activées quand vous souhaitez qu’elles le
soient.
Remarque DisableConstraints et EnableConstraints contrôlent si l’ensemble de données client
impose des contraintes à ses données. Cependant, elles n’ont aucun effet sur le
fait que le serveur d’applications contienne ou non des informations de
contrainte dans les paquets de données. Vous pouvez empêcher le serveur
d’envoyer des contraintes dans le premier emplacement utilisant la propriété
Constraints du fournisseur. Pour plus d’informations sur la gestion des
contraintes depuis le serveur, voir “Gestion des contraintes du serveur” à la
page 15-11. Pour plus d’informations sur la manipulation des contraintes une fois
qu’elles ont été importées, voir “Utilisation des contraintes de serveur” à la
page 19-26.

Ajout de contraintes personnalisées


Vous pouvez utiliser les propriétés de composants des champs de l’ensemble de
données client pour imposer des contraintes sur les valeurs disponibles au-delà
de celles fournies dans les paquets de données depuis le serveur. Chaque
composant champ possède deux propriétés qui peuvent être utilisées pour
spécifier des contraintes :
• La propriété DefaultExpression définit une valeur par défaut qui est attribuée au
champ si l’utilisateur n’en saisit pas une. Remarquez que si le serveur
d’applications attribut aussi une expression par défaut au champ, celle de
l’ensemble de données client est prioritaire car elle est attribuée avant que la
mise à jour ne soit renvoyée au serveur d’applications.
• La propriété CustomConstraint vous permet d’imposer une condition à remplir
pour qu’une valeur de champ puisse être validée. Les contraintes personnalisées
définies de cette façon s’appliquent en plus des contraintes importées depuis le
serveur. Pour plus d’informations sur la manipulation des contraintes
personnalisées sur les composants champ, voir “Création d’une contrainte
personnalisée” à la page 19-25.
En outre, vous pouvez créer des contraintes au niveau de l’enregistrement en
utilisant la propriété Constraints de l’ensemble de données client. Constraints est
une collection d’objets TCheckConstraint, dans laquelle chaque objet représente une
condition distincte. Utilisez la propriété CustomConstraint pour ajouter vos propres
contraintes qui seront vérifiées au moment de la validation des enregistrements.

Création et utilisation d’un ensemble de données client 24-23


Utilisation d’un ensemble de données client avec un fournisseur de données

Mise à jour des enregistrements


Quand une application client est connectée à un serveur d’applications, les
ensembles de données client manipulent une copie locale des données qui leur
est transmise par le serveur d’applications. L’utilisateur consulte et modifie ces
copies dans les contrôles orientés données de l’application client. Si les
contraintes serveur sont activées dans l’ensemble de données client, les
changements d’un utilisateur sont contrôlés en fonction de ces contraintes. Les
changements effectués par un utilisateur sont stockés temporairement par
l’ensemble de données client dans un journal des modifications géré au niveau
interne. Le contenu du journal des modifications est stocké en tant que paquet
de données dans la propriété Delta. Pour rendre permanents les changements
stockés dans Delta, l’ensemble de données client doit les appliquer à la base de
données.
Lorsqu’un client applique les mises à jour au serveur, le processus est le suivant :
1 L’application client appelle la méthode ApplyUpdates qui appartient à un objet
ensemble de données client. Cette méthode transmet le contenu de la propriété
Delta de l’ensemble de données client au serveur d’applications. Delta est un
paquet de données qui contient les enregistrements mis à jour, insérés et
supprimés dans un ensemble de données client.
2 Le composant fournisseur du serveur d’applications applique les mises à jour
dans la base de données, en plaçant en mémoire cache tous les
enregistrements problématiques qu’il ne peut pas résoudre au niveau du
serveur. Voir “Comment répondre aux demandes de mise à jour des clients” à
la page 15-6 pour plus de détails sur l’application des mises à jour par le
serveur.
3 Le composant fournisseur du serveur d’applications renvoie tous les
enregistrements non résolus au client dans un paquet de données Result. Le
paquet de données Result contient tous les enregistrements non mis à jour. Il
contient aussi les informations d’erreur, comme les messages d’erreur et les
codes d’erreur.
4 L’application client essaie de régulariser les erreurs de mise à jour renvoyées
dans le paquet de données Result enregistrement par enregistrement.

Application des mises à jour


Les changements apportés à la copie locale des données de l’ensemble de
données client ne sont transmis au serveur d’applications que lorsque
l’application client appelle la méthode ApplyUpdates pour l’ensemble de données.
ApplyUpdates prend les modifications dans le journal des modifications et les
envoie au serveur d’applications sous forme d’un paquet de données (nommé
Delta).

24-24 Guide du développeur


Utilisation d’un ensemble de données client avec un fournisseur de données

ApplyUpdates accepte un paramètre unique, MaxErrors, qui indique le nombre


maximum d’erreurs que le serveur d’applications peut tolérer avant de mettre fin
au processus de mise à jour. Si MaxErrors est égal à 0, tout le processus de mise
à jour prend fin dès qu’une erreur de mise à jour se produit sur le serveur
d’applications. Aucune modification n’est écrite dans la base de données et le
journal des modifications de l’ensemble de données client reste inchangé. Si
MaxErrors est égal à -1, un nombre quelconque d’erreurs est toléré et le journal
des modifications contient tous les enregistrements n’ayant pas pu être
appliqués. Si MaxErrors a une valeur positive et qu’il se produit davantage
d’erreurs que le nombre autorisé par MaxErrors, toutes les mises à jour sont
annulées. S’il se produit moins d’erreurs que le nombre spécifié par MaxErrors,
tous les enregistrements qui s’appliquent correctement sont automatiquement
effacés du journal des modifications de l’ensemble de données client.
ApplyUpdates renvoie le nombre réel d’erreurs rencontrées, qui est toujours
inférieur ou égal à MaxErrors plus un. Cette valeur est initialisée pour indiquer
le nombre d’enregistrements qui n’ont pas pu être écrits dans la base de
données. Le serveur d’applications renvoie aussi ces enregistrements à l’ensemble
de données client.
L’ensemble de données client a la responsabilité de régulariser les
enregistrements générant des erreurs. ApplyUpdates appelle la méthode Reconcile
pour écrire les mises à jour dans la base de données. Reconcile est une routine de
gestion d’erreur qui appelle indirectement la fonction ApplyUpdates d’un
composant fournisseur situé sur le serveur d’applications. La fonction
ApplyUpdates du composant fournisseur écrit les mises à jour dans la base de
données et tente de corriger les erreurs rencontrées. Les enregistrements qu’elle
ne peut appliquer à cause des erreurs sont renvoyés à la méthode Reconcile de
l’ensemble de données client. Reconcile essaie alors de corriger les erreurs
restantes en appelant le gestionnaire d’événement OnReconcileError. Vous devez
écrire le code du gestionnaire d’événement OnReconcileError pour qu’il corrige les
erreurs. Pour plus d’informations sur la création et l’utilisation de
OnReconcileError, voir “Régularisation des erreurs de mise à jour” à la page 24-26.
Enfin, Reconcile supprime du journal des modifications celles dont l’application a
réussi et met à jour Data avec les nouveaux enregistrements mis à jour. Quand
Reconcile est terminée, ApplyUpdates indique le nombre d’erreurs survenues.
Remarque Si vous utilisez des transactions MTS ou partagez l’instance d’un serveur
d’applications avec d’autres clients, vous voudrez peut être échanger avec le
fournisseur sur le serveur d’applications des informations d’état persistantes,
avant ou après l’application des mises à jour. L’ensemble de données client reçoit
un événement BeforeApplyUpdates avant que les mises à jour ne soient envoyées,
ce qui vous permet d’envoyer au serveur des informations d’état persistantes.
Une fois que les mises à jour sont appliquées (mais avant le processus de
régularisation), l’ensemble de données client reçoit un événement
AfterApplyUpdates, ce qui vous permet de répondre à toute information d’état
persistante renvoyée par le serveur d’applications.

Création et utilisation d’un ensemble de données client 24-25


Utilisation d’un ensemble de données client avec un fournisseur de données

Régularisation des erreurs de mise à jour


Le fournisseur sur le serveur d’applications renvoie les enregistrements et les
informations d’erreur à l’ensemble de données client dans un paquet de données
de résultat. Si le serveur d’applications renvoie un nombre d’erreurs supérieur à
zéro, l’événement OnReconcileError de l’ensemble de données client intervient
pour chaque enregistrement du paquet de données de résultat.
Il faut toujours coder le gestionnaire d’événement OnReconcileError, même s’il ne
fait que rejeter les enregistrements renvoyés par le serveur d’applications. Le
gestionnaire d’événement OnReconcileError reçoit quatre paramètres :
• DataSet : ensemble de données client auquel les mises à jour sont appliquées.
Vous pouvez utiliser les méthodes de l’ensemble de données client pour
obtenir des informations sur les enregistrements problématiques et pour y
apporter des modifications afin de résoudre tous les problèmes. Vous pouvez
notamment utiliser les propriétés CurValue, OldValue et NewValue des champs
de l’enregistrement en cours pour déterminer la cause du problème de mise à
jour. Toutefois, vous ne pouvez pas appeler une méthode de l’ensemble de
données client qui modifie l’enregistrement en cours dans un gestionnaire
d’événement OnReconcileError.
• E: objet EReconcileError représentant le problème survenu. Vous pouvez utiliser
cette exception pour extraire un message d’erreur ou pour déterminer la cause
d’une erreur de mise à jour.
• UpdateKind: type de mise à jour ayant généré l’erreur. UpdateKind peut être
ukModify (problème survenu lors de la mise à jour d’un enregistrement
existant modifié), ukInsert (problème survenu lors de l’insertion d’un nouvel
enregistrement) ou ukDelete (problème survenu lors de la suppression d’un
enregistrement existant).
• Action: paramètre var vous permettant d’indiquer l’action à entreprendre à la
fin de l’exécution du gestionnaire OnReconcileError. Lors de l’entrée dans le
gestionnaire, Action est initialisée à l’action entreprise par le processus de
résolution du serveur. Dans votre gestionnaire d’événement, vous initialisez ce
paramètre pour
• Ignorer l’enregistrement et le laisser dans le journal de modifications (raSkip).
• Abandonner l’opération de régularisation. (raAbort)
• Fusionner la modification infructueuse avec l’enregistrement correspondant
sur le serveur. (raMerge) Cela ne fonctionne que si le serveur n’a changé
aucun des champs modifiés par l’utilisateur.
• Remplacer la mise à jour courante dans le journal de modifications par la
valeur de l’enregistrement dans le gestionnaire d’événement (qui a
normalement été corrigé). (raCorrect)
• Retirer les modifications de l’enregistrement sur l’ensemble de données
client, en réappliquant les valeurs initialement fournies. (raCancel)
• Mettre à jour la valeur de l’enregistrement en cours en fonction de
l’enregistrement du serveur. (raRefresh)

24-26 Guide du développeur


Utilisation d’un ensemble de données client avec un fournisseur de données

Le code suivant montre un gestionnaire d’événement OnReconcileError qui utilise


la boîte de dialogue de régularisation de l’unité RecError présente dans le
répertoire du référentiel d’objets. Pour utiliser cette boîte de dialogue, ajoutez
RecError à votre clause uses.
procedure TForm1.ClientDataSetReconcileError(DataSet: TClientDataSet; E: EReconcileError;
UpdateKind: TUpdateKind, var Action TReconcileAction);
begin
Action := HandleReconcileError(DataSet, UpdateKind, E);
end;

Rafraîchissement des enregistrements


Les applications client manipulent une photographie mémorisée des données du
serveur d’applications. Au fur et à mesure que le temps passe, d’autres
utilisateurs peuvent modifier les données de sorte que les données de
l’application client deviennent une représentation de moins en moins fidèle des
données sous-jacentes.
Comme tout ensemble de données, les ensembles de données client disposent
d’une méthode Refresh qui met à jour les enregistrements en fonction des valeurs
courantes sur le serveur. Toutefois, l’appel de Refresh ne fonctionne que si le
journal de modifications ne contient aucun changement. L’appel de Refresh alors
que des modifications n’ont pas été appliquées déclenche une exception.
Les applications client peuvent aussi mettre à jour les données sans toucher au
journal de modifications. Pour ce faire, appelez la méthode RefreshRecord de
l’ensemble de données client. A la différence de la méthode Refresh, RefreshRecord
ne met à jour que l’enregistrement en cours dans l’ensemble de données client.
RefreshRecord modifie la valeur d’enregistrement initialement obtenue du serveur
d’applications mais laisse intacts tous les changements contenus dans le journal
de modifications.
Attention Il n’est pas approprié d’appeler systématiquement RefreshRecord. Si les modifications
de l’utilisateur entrent en conflit avec celles apportées à l’ensemble de données sous-
jacent par d’autres utilisateurs, l’appel de RefreshRecord masque ce conflit. Lorsque
l’application client applique ses mises à jour, aucune erreur de régularisation ne se
produit et l’application ne peut pas résoudre le conflit.
Pour éviter que les erreurs de mise à jour ne soient masquées, les applications
client peuvent vérifier qu’aucune mise à jour n’est en attente avant d’appeler
RefreshRecord. Par exemple, le code suivant déclenche une exception en cas de
tentative de rafraîchissement d’un enregistrement modifié :
if ClientDataSet1.UpdateStatus <> usUnModified then
raise Exception.Create('Vous devez appliquer les mises à jour avant de rafraîchir
l’enregistrement en cours.');
ClientDataSet1.RefreshRecord;

Création et utilisation d’un ensemble de données client 24-27


Utilisation d’un ensemble de données client avec des données de fichier linéaire

Communication avec des fournisseurs à l’aide d’événements


personnalisés
Les ensembles de données client offrent de nombreuses possibilités de
personnalisation de la communication entre l’application client et le serveur
d’applications. Avant et après chaque appel de la méthode IAppServer vers le
fournisseur de l’ensemble de données client, ce dernier reçoit des événements
spéciaux destinés à lui permettre d’échanger des informations arbitraires avec
son fournisseur. Ces événements correspondent à des événements similaires du
fournisseur. Et, par exemple, quand l’ensemble de données client appelle sa
méthode ApplyUpdates, les événements suivants se produisent :
1 L’ensemble de données client reçoit un événement BeforeApplyUpdates, où il
spécifie des informations arbitraires personnalisées dans un OleVariant nommé
OwnerData.
2 Le fournisseur reçoit un événement BeforeApplyUpdates, où il peut répondre à
l’OwnerData depuis l’ensemble de données client et mettre à jour la valeur de
l’OwnerData avec les nouvelles informations.
3 Le fournisseur poursuit son processus normal d’assemblage d’un paquet de
données (comprenant tous les événements qui l’accompagnent).
4 Le fournisseur reçoit un événement AfterApplyUpdates, où il peut répondre à la
valeur courante de l’OwnerData et la mettre à jour en lui donnant une valeur
pour le client.
5 L’ensemble de données client reçoit un événement AfterApplyUpdates, où il
peut répondre à la valeur renvoyée de l’OwnerData.
Les appels aux autres méthodes d’IAppServer sont accompagnés d’un ensemble
semblable d’événements BeforeXXX et AfterXXX qui vous permettent de
personnaliser la communication entre client et serveur.
En outre, l’ensemble de données client dispose d’une méthode spéciale,
DataRequest, dont le seul but est de permettre une communication avec le
fournisseur spécifique à l’application. Quand l’ensemble de données client
appelle DataRequest, il transmet un OleVariant en tant que paramètre pouvant
contenir les informations que vous voulez. En retour, est généré l’événement
OnDataRequest sur le fournisseur, où vous pouvez répondre d’une façon
quelconque définie dans l’application et renvoyer une valeur à l’ensemble de
données client.

Utilisation d’un ensemble de données client avec des données de


fichier linéaire
Les ensembles de données client peuvent fonctionner indépendamment d’un
fournisseur, comme dans les applications de base de données à fichiers linéaires
ou celles basées sur le modèle “briefcase”. Lorsque il n’y a pas de fournisseur,
toutefois, l’application client ne peut pas obtenir de définitions de table et de

24-28 Guide du développeur


Utilisation d’un ensemble de données client avec des données de fichier linéaire

données du serveur et aucun serveur ne peut recevoir ses mises à jour.


L’ensemble de données doit indépendamment :
• Définir et créer les tables.
• Charger les données enregistrées.
• Fusionner les modifications dans ses données.
• Enregistrer les données.

Création d’un nouvel ensemble de données


Il existe trois façons de définir et créer des ensembles de données client qui
n’obtiennent pas leurs données d’un composant fournisseur :
• Vous pouvez copier un ensemble de données existant (lors de la conception
ou à l’exécution). Voir “Copie de données d’un autre ensemble de données” à
la page 24-14 pour plus d’informations sur la copie d’ensembles de données
existants.
• Vous pouvez définir et créer un nouvel ensemble de données client en créant
des champs persistants pour l’ensemble de données puis en choisissant Créer
le Dataset dans son menu contextuel. Voir “Création d’un nouvel ensemble de
données à l’aide de champs persistants” à la page 13-16 pour plus de détails.
• Vous pouvez définir et créer un nouvel ensemble de données client à partir
des définitions de champs et d’index. Voir “Création d’un ensemble de
données à l’aide de définitions de champ et d’index” à la page 13-17 pour
plus de détails.

Chargement des données depuis un fichier ou un flux


Pour charger des données depuis un fichier, appelez la méthode LoadFromFile de
l’ensemble de données client. LoadFromFile accepte un paramètre : une chaîne qui
spécifie le nom du fichier depuis lequel les données doivent être lues (le cas
échéant, le nom de fichier peut être un nom de chemin d’accès qualifié). Si vous
chargez toujours les données de l’ensemble de données client à partir du même
fichier, vous pouvez utiliser la propriété FileName. Si FileName nomme un fichier
existant, les données sont automatiquement chargées à l’ouverture de l’ensemble
de données client.
Pour charger des données à partir d’un flux, appelez la méthode LoadFromStream
de l’ensemble de données client. LoadFromStream accepte un paramètre : un objet
flux qui fournit les données.
Les données chargées par LoadFromFile (LoadFromStream) doivent avoir été
sauvegardées dans le format de données ensemble de données client par cet
ensemble de données client ou par un autre en utilisant la méthode SaveToFile
(SaveToStream). Pour plus d’informations sur la sauvegarde des données dans un
fichier ou dans un flux, voir “Sauvegarde des données dans un fichier ou un
flux” à la page 24-30.

Création et utilisation d’un ensemble de données client 24-29


Utilisation d’un ensemble de données client avec des données de fichier linéaire

Lorsque vous appelez LoadFromFile ou LoadFromStream, toutes les données du


fichier sont lues dans la propriété Data. Toutes les modifications qui figuraient
dans le journal de modifications lorsque les données ont été sauvegardées sont
lues dans la propriété Delta.

Fusion des modifications dans les données


Lorsque vous modifiez des données dans un ensemble de données client, les
modifications sont enregistrées dans le journal de modifications mais n’affectent
pas les données originales.
Pour que les modifications soient permanentes, appelez MergeChangeLog.
MergeChangeLog écrase les enregistrements de Data avec les valeurs des champs
du journal de modifications.
Après l’exécution de MergeChangeLog, la propriété Data contient un amalgame
constitué des données existantes et des modifications issues du journal de
modifications. Cet amalgame devient la nouvelle valeur de la propriété Data (elle
sert ensuite de référence pour les futures modifications). MergeChangeLog efface
tous les enregistrements du journal de modifications et réinitialise la propriété
ChangeCount à 0.
Attention MergeChangeLog ne doit pas être appelée lorsqu’une application client est
connectée à un serveur d’applications. Dans ce cas, vous devez appeler la
méthode ApplyUpdates pour écrire les modifications dans la base de données.
Pour plus d’informations, voir “Application des mises à jour” à la page 24-24.
Remarque Il est également possible de fusionner les modifications dans les données d’un
ensemble de données client séparé si ce dernier a initialement fourni les données
dans la propriété Data. Pour ce faire, vous devez utiliser un fournisseur
d’ensembles de données et un résolveur. Voir un exemple dans “Affectation
directe des données” à la page 24-15.

Sauvegarde des données dans un fichier ou un flux


Si un ensemble de données client est utilisé dans une application à niveau
unique, les modifications et les fusions de données n’existent qu’en mémoire.
Pour que les modifications soient permanentes, vous devez les enregistrer. Vous
pouvez le faire par l’intermédiaire de la méthode SaveToFile.
SaveToFile accepte un paramètre : une chaîne qui spécifie le fichier dans lequel les
données sont écrites (le cas échéant, le nom de fichier peut être un nom de
chemin d’accès qualifié). Si le fichier existe déjà, son contenu actuel est écrasé.
Si vous sauvegardez toujours les données dans le même fichier, vous pouvez
utiliser la propriété FileName. Si FileName est initialisée, les données sont
automatiquement sauvegardées dans le fichier nommé à la fermeture de
l’ensemble de données client.

24-30 Guide du développeur


Utilisation d’un ensemble de données client avec des données de fichier linéaire

Vous pouvez aussi sauvegarder les données dans un flux à l’aide de la méthode
SaveToStream. SaveToStream accepte un paramètre : un objet flux qui reçoit les
données.
Remarque Si vous sauvegardez un ensemble de données client alors que des modifications
existent encore dans le journal de modifications, ces modifications ne sont pas
fusionnées avec les données. Lorsque vous rechargez les données à l’aide de la
méthode LoadFromFile ou LoadFromStream, le journal de modifications contient
toujours les modifications non fusionnées. Ce point est important pour les
applications qui supportent le modèle “briefcase”, dans lequel les modifications
finissent par être appliquées à un composant fournisseur sur le serveur
d’applications.
Remarque SaveToFile ne conserve aucun index ajouté à l’ensemble de données client.

Création et utilisation d’un ensemble de données client 24-31


24-32 Guide du développeur
Chapitre

Manipulation des mises à jour


Chapter 25
25
en mémoire cache
Les mises à jour en mémoire cache vous permettent d’extraire des données d’une
base de données, de les placer en mémoire cache et de les éditer localement, puis
d’appliquer les modifications à la base de données. Lorsque cette fonctionnalité
est activée, les mises à jour d’un ensemble de données (par exemple, l’écriture de
modifications ou la suppression d’enregistrements) sont placées dans un cache
interne au lieu d’être écrites directement dans la table sous-jacente de l’ensemble
de données. Une fois les modifications terminées, votre application fait appel à
une méthode qui les écrit dans la base de données et efface le contenu de la
mémoire cache.
Ce chapitre décrit quant et comment utiliser les mises à jour en mémoire cache,
ainsi que le composant TUpdateSQL que vous pouvez utiliser avec les mises à
jour en mémoire cache pour mettre à jour n’importe quel ensemble de données
(notamment ceux qu’il n’est normalement pas possible de mettre à jour).

Quand utiliser les mises à jour en mémoire cache ?


L’intérêt des mises à jour en mémoire cache est essentiellement de réduire les
goulets d’étranglement sur les serveurs de bases de données distants en :
• minimisant la durée des transactions ;
• réduisant le volume du trafic réseau.

Manipulation des mises à jour en mémoire cache 25-1


Utilisation des mises à jour en mémoire cache

Alors que les mises à jour en mémoire cache peuvent minimiser la durée des
transactions et réduire considérablement le trafic réseau, elles ne sont pas
appropriées à toutes les applications client de bases de données fonctionnant sur
des serveurs distants. Les trois considérations suivantes doivent être prises en
compte avant de décider d’utiliser les mises à jour en mémoire cache :
• Les données en mémoire cache sont locales à votre application et ne sont
pas sous contrôle transactionnel. Dans un environnement client/serveur à fort
trafic, cela implique les situations suivantes :
• les autres applications peuvent modifier les données réelles sur le serveur
pendant que vos utilisateurs éditent leur version locale des données ;
• les autres applications ne peuvent pas voir les modifications effectuées par
la vôtre tant qu’elles n’ont pas été appliquées.
• Dans une relation maître/détail, il peut être compliqué de gérer l’ordre
d’application des mises à jour en mémoire cache. C’est surtout le cas pour
les relations maître/détail imbriquées dans lesquelles une table détail est la
table maître d’une autre table détail et ainsi de suite.
• L’application des mises à jour en mémoire cache à des ensembles de
données en lecture seule basés sur des requêtes nécessite l’utilisation
d’objets mise à jour.
Les composants d’accès aux données fournissent des méthodes de mise à jour en
mémoire cache et de contrôle des transactions que vous pouvez introduire dans
le code de votre application afin de gérer ces situations ; toutefois, vous devez
veiller à couvrir tous les scénarios que votre application est susceptible de
rencontrer dans votre environnement de travail.

Utilisation des mises à jour en mémoire cache


Cette section explique comment les mises à jour en mémoire cache fonctionnent
dans une application. Si vous utilisez les mises à jour en mémoire cache pour la
première fois, cette description pourra vous servir de référence lors de leur
implémentation dans vos applications.
Pour implémenter les mises à jour en mémoire cache dans une application, vous
devez suivre les étapes présentées ci-après dans l’ordre dans lequel elles sont
énoncées :
1 Activation des mises à jour en mémoire cache. L’activation des mises à jour
en mémoire cache provoque une transaction en lecture seule qui extrait du
serveur les données à afficher, puis se termine. Les copies locales des données
sont stockées en mémoire à des fins d’affichage et d’édition. Pour plus
d’informations sur l’activation et la désactivation des mises à jour en mémoire
cache, voir “Activation et désactivation des mises à jour en mémoire cache” à
la page 25-3.

25-2 Guide du développeur


Utilisation des mises à jour en mémoire cache

2 Affichage et édition des copies locales des enregistrements. Vous pouvez


alors insérer de nouveaux enregistrements et supprimer des enregistrements
existants. La copie originale de chaque enregistrement et les modifications qui
lui sont apportées sont stockées en mémoire. Pour plus d’informations sur
l’affichage et l’édition de données lorsque les mises à jour en mémoire cache
sont activées, voir “Application des mises à jour en mémoire cache” à la
page 25-5.
3 Extraction d’enregistrements supplémentaires au fur et à mesure des
besoins. Lorsqu’un utilisateur fait défiler des enregistrements, des
enregistrements supplémentaires sont extraits au fur et à mesure des besoins.
Chaque extraction se produit dans le contexte d’une autre transaction (une
transaction de courte durée et en lecture seule). Une application peut
éventuellement extraire tous les enregistrements à la fois plutôt que de les
extraire par petits lots. Pour plus d’informations sur l’extraction de tous les
enregistrements, voir “Extraction d’enregistrements” à la page 25-4.
4 Poursuite de l’affichage et de l’édition des copies locales des
enregistrements jusqu’à ce que toutes les modifications soient terminées.
5 Application à la base de données de tous les enregistrements placés en
mémoire cache ou annulation des mises à jour. Pour chaque enregistrement
écrit dans la base de données, un événement OnUpdateRecord est déclenché. Si
une erreur se produit lors de l’écriture d’un enregistrement dans la base de
données, l’événement OnUpdateError est déclenché, ce qui permet à
l’application de corriger l’erreur, si possible, et de poursuivre la mise à jour.
Lorsque les mises à jour sont terminées, toutes les mises à jour qui ont été
appliquées avec succès sont effacées de la mémoire cache locale. Pour plus
d’informations sur l’application des mises à jour dans la base de données, voir
“Application des mises à jour en mémoire cache” à la page 25-5.
Si une application annule les modifications au lieu de les appliquer, la copie
des enregistrements placés en mémoire cache et leurs modifications sont
libérées sans écriture des modifications dans la base de données. Pour plus
d’informations sur l’annulation des mises à jour, voir “Annulation des mises à
jour en mémoire cache en suspens” à la page 25-9.

Activation et désactivation des mises à jour en mémoire cache


Les mises à jour en mémoire cache sont activées et désactivées par la propriété
CachedUpdates d’un composant TTable, TQuery et TStoredProc. La propriété
CachedUpdates est à False par défaut, ce qui signifie que les mises à jour en
mémoire cache ne sont pas activées pour un ensemble de données.
Remarque Les ensembles de données client placent toujours les mises à jour en mémoire
cache. Ils ne disposent pas de la propriété CachedUpdates car ils ne peuvent
désactiver l’utilisation du cache des mises à jour pour un ensemble de données
client.

Manipulation des mises à jour en mémoire cache 25-3


Utilisation des mises à jour en mémoire cache

Pour utiliser les mises à jour en mémoire cache, CachedUpdates doit avoir la
valeur True, soit au moment de la conception (via l’inspecteur d’objets), soit à
l’exécution. Lorsque vous mettez CachedUpdates à True, l’événement
OnUpdateRecord de l’ensemble de données est déclenché s’il existe. Pour plus
d’informations sur l’événement OnUpdateRecord, voir “Création d’un gestionnaire
d’événement OnUpdateRecord” à la page 25-26.
Par exemple, le code ci-dessous active les mises à jour en mémoire cache pour
un ensemble de données :
CustomersTable.CachedUpdates := True;
Lorsque vous activez les mises à jour en mémoire cache, une copie de tous les
enregistrements est placée (ou cachée) dans la mémoire locale. C’est cette copie
des données que les utilisateurs voient et éditent. Les modifications, les
insertions et les suppressions sont aussi placées en mémoire cache. Elles
s’accumulent en mémoire jusqu’à ce que la mémoire cache en cours des
modifications soit appliquée à la base de données. Si les enregistrements
modifiés sont appliqués avec succès dans la base de données, l’enregistrement
contenant ces modifications dans la mémoire cache est libéré.
Remarque L’application des mises à jour en mémoire cache ne provoque pas la
désactivation des mises à jour suivantes. Elle ne fait qu’écrire les modifications
en cours et entraîne leur effacement de la mémoire.
Pour désactiver les mises à jour en mémoire cache pour un ensemble de
données, donnez à CachedUpdates la valeur False. Si vous désactivez les mises à
jour en mémoire cache avant d’appliquer les modifications en suspens, ces
dernières sont supprimées sans notification. Votre application peut tester la
propriété UpdatesPending avant de désactiver les mises à jour en mémoire cache.
Par exemple, le code ci-dessous demande confirmation avant de désactiver les
mises à jour en mémoire cache pour un ensemble de données :
if (CustomersTable.UpdatesPending)
if (Application.MessageBox(“Annuler mises à jour en suspens?”,
“Modifications non validées”,
MB_YES + MB_NO) = IDYES) then
CustomersTable.CachedUpdates = False;

Extraction d’enregistrements
Si les mises à jour en mémoire cache sont activées, les ensembles de données
BDE gèrent par défaut l’extraction automatique des données d’une base de
données en fonction des besoins. Les ensembles de données extraient
suffisamment d’enregistrements pour l’affichage. Lors du traitement, de
nouvelles extractions peuvent se produire. Si votre application a des besoins
spécifiques, elle peut extraire tous les enregistrements à la fois en appelant la
méthode FetchAll de l’ensemble de données. FetchAll crée en mémoire une copie
locale de tous les enregistrements de l’ensemble de données. Si un ensemble de
données contient beaucoup d’enregistrements ou des enregistrements comportant
des champs BLOB volumineux, il n’est pas recommandé d’utiliser FetchAll.

25-4 Guide du développeur


Utilisation des mises à jour en mémoire cache

Les ensembles de données client utilisent la propriété PacketRecords pour indiquer


le nombre d’enregistrements devant être lus à tout moment. Si la valeur True est
affectée à la propriété FetchOnDemand, l’ensemble de données client gère
automatiquement les données quand cela est nécessaire. Sinon, il est possible
d’utiliser la méthode GetNextPacket pour lire les enregistrements sur le serveur de
données. Pour davantage d’informations sur la lecture d’enregistrements en
utilisant un ensemble de données client, voir “Extraction des données depuis un
serveur d’applications” à la page 24-20.

Application des mises à jour en mémoire cache


Lorsqu’un ensemble de données est en mode mise à jour en mémoire cache, les
modifications ne sont pas écrites dans la base de données tant que votre
application n’a pas explicitement fait appel à des méthodes pour les appliquer. En
principe, une application n’effectue la mise à jour qu’à la demande de l’utilisateur,
par exemple par un clic sur un bouton ou par l’activation d’un élément de menu.
Important Pour appliquer les mises à jour à un ensemble d’enregistrements extrait par une
requête SQL ne renvoyant pas d’ensemble de résultat modifiable, vous devez
utiliser un objet TUpdateSQL pour spécifier comment effectuer la mise à jour.
Dans le cas où les mises à jour s’appliquent à des jointures (c’est-à-dire des
requêtes impliquant deux ou plusieurs tables), vous devez fournir un objet
TUpdateSQL pour chaque table impliquée, et utiliser le gestionnaire d’événement
OnUpdateRecord pour demander à ces objets d’effectuer les mises à jour. Pour
plus d’informations, voir “Mise à jour d’un ensemble de résultat en lecture
seule” à la page 25-25. Pour plus d’informations sur la création et l’utilisation
d’un gestionnaire d’événement OnUpdateRecord, voir “Création d’un gestionnaire
d’événement OnUpdateRecord” à la page 25-26.
L’application des mises à jour se fait en deux temps. Elle doit avoir lieu depuis le
contrôle transactionnel d’un composant base de données afin de permettre à votre
application de récupérer les données en cas d’erreur. Pour en savoir plus sur la
gestion des transactions avec les composants base de données, voir “Interactions
entre les composants base de données et les composants session” à la page 17-10.
Lors de l’application des mises à jour sous contrôle transactionnel, les
événements suivants se produisent :
1 Une transaction de base de données est lancée.
2 Les mises à jour en mémoire cache sont écrites dans la base de données
(phase 1). S’il est fourni, l’événement OnUpdateRecord est déclenché pour
chaque enregistrement écrit dans la base de données. Si une erreur se produit
lorsqu’un enregistrement est appliqué à la base de données, l’événement
OnUpdateError est déclenché.
Si l’écriture dans la base de données échoue :
• les modifications de la base de données sont annulées, ce qui met fin à la
transaction ;
• les mises à jour en mémoire cache ne sont pas validées et restent intactes
dans le tampon interne du cache.

Manipulation des mises à jour en mémoire cache 25-5


Utilisation des mises à jour en mémoire cache

Si l’écriture dans la base de données a lieu normalement :


• les modifications dans la base de données sont validées, ce qui met fin à la
transaction ;
• les mises à jour en mémoire cache sont validées, ce qui a pour effet
d’effacer le contenu du tampon interne du cache (phase 2).
Cette approche en deux temps de la mise à jour en mémoire cache permet la
récupération des données en cas d’erreur, en particulier lors de la mise à jour
d’ensembles de données multiples (par exemple, des ensembles de données
associés à une fiche maître-détail). Pour en savoir plus sur la gestion des erreurs
lors de l’application de mises à jour en mémoire cache, reportez-vous à “Gestion
des erreurs de mise à jour en mémoire cache” à la page 25-28.
Les mises à jour en mémoire cache peuvent être appliquées de deux façons
différentes. Pour appliquer les mises à jour à un ensemble de données associé à
un composant base de données, appelez la méthode ApplyUpdates du composant
base de données. Pour appliquer les mises à jour à un seul ensemble de données,
appelez ses méthodes ApplyUpdates et Commit. Ces deux possibilités sont décrites
dans les sections suivantes.

Utilisation de la méthode d’un composant base de données


En temps normal, les applications placent les mises à jour en mémoire cache au
niveau de l'ensemble de données. Toutefois, il est parfois important d'appliquer
les mises à jour à plusieurs ensembles de données connexes dans le contexte
d'une transaction unique. Par exemple, lors de la manipulation de fiches maître/
détail, il est naturel de vouloir valider les modifications dans la table maître et
dans la table détail.
Pour appliquer des mises à jour en mémoire cache à un ou plusieurs ensembles
de données lors d’une connexion à une base de données, vous devez faire appel
à la méthode ApplyUpdates du composant base de données. Le code ci-dessous
applique la mise à jour de l’ensemble de données CustomerQuery en réponse à un
clic sur un bouton :
procedure TForm1.ApplyButtonClick(Sender: TObject);
begin
// pour les bases de données locales comme Paradox, dBASE ou FoxPro
// mettre TransIsolation à DirtyRead
if not (Database1.IsSQLBased) and not ( Database1.TransIsolation = tiDirtyRead) then
Database1.TransIsolation := tiDirtyRead;
Database1.ApplyUpdates([CustomersQuery]);
end;
La séquence précédente lance une transaction et écrit les mises à jour en
mémoire cache dans la base de données. En cas de réussite, elle valide également
la transaction, puis les mises à jour en mémoire cache. En cas d’échec, elle
annule la transaction et ne change pas l’état des mises à jour en mémoire cache.
Dans ce cas, votre application doit pouvoir gérer les erreurs de mise à jour en
mémoire cache au moyen de l’événement OnUpdateError d’un ensemble de
données. Pour en savoir plus sur la gestion des erreurs de mise à jour, reportez-
vous à “Gestion des erreurs de mise à jour en mémoire cache” à la page 25-28.

25-6 Guide du développeur


Utilisation des mises à jour en mémoire cache

Le principal avantage d’appeler la méthode ApplyUpdates d’un composant base


de données est que vous pouvez mettre à jour les composants ensemble de
données associés à la base de données. Le paramètre de la méthode ApplyUpdates
est un tableau de TDBDataSet. Par exemple, le code ci-dessous applique une mise
à jour à deux requêtes utilisées dans une fiche maître-détail :
if not (Database1.IsSQLBased) and not ( Database1.TransIsolation = tiDirtyRead) then
Database1.TransIsolation := tiDirtyRead;
Database1.ApplyUpdates([CustomerQuery, OrdersQuery]);
Pour plus d’informations sur la mise à jour des tables maître/détail, voir
“Application des mises à jour à des tables maître / détail” à la page 25-8.

Utilisation des méthodes d’un composant ensemble de données


Vous pouvez appliquer les mises à jour d’ensembles de données individuels en
utilisant les méthodes ApplyUpdates et CommitUpdates de l’ensemble de données.
Chacune de ces méthodes encapsule une phase du processus de mise à jour :
1 ApplyUpdates écrit dans une base de données les modifications effectuées en
mémoire cache (phase 1) ;
2 CommitUpdates efface le contenu du cache interne une fois l’opération
d’écriture terminée (phase 2).
En appliquant les mises à jour au niveau de l’ensemble de données, vous êtes en
mesure de contrôler l’ordre dans lequel elles sont appliquées à chaque ensemble
de données. L’ordre d’application des mises à jour est un élément critique pour
la gestion des relations maître/détail. Pour garantir que les mises à jour
s’effectuent dans un ordre correct, il est conseillé de les appliquer au niveau de
l’ensemble de données. Pour plus d’informations, voir “Application des mises à
jour à des tables maître / détail” à la page 25-8.
Le code ci-dessous illustre comment appliquer les mises à jour dans une
transaction pour l’ensemble de données CustomerQuery :
procedure TForm1.ApplyButtonClick(Sender: TObject)
begin
Database1.StartTransaction;
try
if not (Database1.IsSQLBased) and not ( Database1.TransIsolation = tiDirtyRead) then
Database1.TransIsolation := tiDirtyRead;
CustomerQuery.ApplyUpdates; { tentative d’écriture des mises à jour dans
la base de données }
Database1.Commit; { en cas de réussite, validation
des modifications }
except
Database1.Rollback; { en cas d’échec, annulation des
modifications }
raise; { l’exception est à nouveau provoquée pour empêcher l’appel à
CommitUpdates }
end;
CustomerQuery.CommitUpdates; { en cas de réussite, effacement du
cache interne }
end;

Manipulation des mises à jour en mémoire cache 25-7


Utilisation des mises à jour en mémoire cache

Si une exception est provoquée au cours de l’appel à ApplyUpdates, la transaction


de base de données est annulée pour garantir que la table sous-jacente restera
inchangée. L’instruction raise du bloc try...except a pour effet de redéclencher
l’exception, empêchant ainsi l’appel à CommitUpdates. Comme cet appel n’a pas
lieu, le cache interne de la mise à jour n’est pas vidé et vous pouvez gérer les
erreurs, puis éventuellement faire une nouvelle tentative.

Application des mises à jour à des tables maître / détail


Lorsque les mises à jour sont appliquées à des tables maître/détail, l’ordre dans
lequel les ensembles de données sont énumérés est déterminant. En principe,
vous devez toujours mettre à jour les tables maître avant les tables détail, sauf
lorsque vous gérez la suppression d’enregistrements. Cette règle s’applique
également dans le cas de relations maître/détail complexes dans lesquelles une
table détail est la table maître d’une autre table détail.
Les tables maître/détail peuvent être mises à jour au niveau des composants
ensemble de données ou au niveau des composants base de données. Pour un
plus grand contrôle et afin de créer du code auto-documenté, vous devez
appliquer les mises à jour au niveau des ensembles de données. L’exemple
suivant illustre comment coder les mises à jour en mémoire cache pour deux
tables, Master et Detail, impliquées dans une relation maître/détail :
Database1.StartTransaction;
try
Master.ApplyUpdates;
Detail.ApplyUpdates;
Database1.Commit;
except
Database1.Rollback;
raise;
end;
Master.CommitUpdates;
Detail.CommitUpdates;
Si une erreur se produit lorsque des mises à jour sont appliquées, ce code laisse
le cache et les données sous-jacentes des tables dans l’état antérieur aux appels à
ApplyUpdates.
Si une exception est déclenchée lors de l’appel à Master.ApplyUpdates, elle est
gérée comme dans l’exemple précédent. Supposons, cependant, que l’appel à
Master.ApplyUpdates donne le résultat escompté et que l’appel suivant à
Detail.ApplyUpdates échoue. Les modifications sont appliquées à la table maître.
Comme toutes les données sont mises à jour à l’intérieur d’une transaction de
bases de données, même les changements apportés à la table maître sont annulés
lorsque Detail.ApplyUpdates est appelée dans le bloc except. De plus,
UpdatesMaster.CommitUpdates n’est pas appelée, car l’exception déclenchée une
deuxième fois a pour effet d’omettre ce code ; le cache reste donc dans l’état où
il se trouvait avant la tentative de mise à jour.

25-8 Guide du développeur


Utilisation des mises à jour en mémoire cache

Pour apprécier tout l’intérêt de la mise à jour en deux temps, supposez un


instant que ApplyUpdates soit un processus en une seule phase qui mette à jour à
la fois les données et le cache. Si c’est le cas et si une erreur se produit lors de
l’application de la mise à jour à la table Detail, il n’y aura plus aucun moyen de
remettre les données et le cache dans leur état initial. Même si l’appel à
Database1.Rollback a pour effet de restaurer la base de données, il n’y a alors
aucun moyen de restaurer le cache.

Annulation des mises à jour en mémoire cache en suspens


Les mises à jour en mémoire cache en suspens correspondent à des enregistrements
modifiés qui sont émis dans la mémoire cache sans être appliqués à la base de
données. Elles peuvent être annulées en utilisant les trois moyens suivants :
• Mettez la propriété CachedUpdates à False pour annuler toutes les mises à jour
en suspens et désactiver les mises à jour suivantes.
• Appelez la méthode CancelUpdates pour annuler toutes les mises à jour en
suspens sans désactiver les futures mises à jour en mémoire cache.
• Appelez RevertRecord pour annuler les mises à jour en mémoire cache
apportées à l’enregistrement en cours.
Les sections suivantes présentent ces options en détail.

Annulation des mises à jour en suspens et désactivation des mises à jour


suivantes
Pour annuler les futures mises à jour en mémoire cache et supprimer toutes les
mises à jour en suspens sans les appliquer, mettez la propriété CachedUpdates à
False. Lorsque CachedUpdates est à False, la méthode CancelUpdates est
automatiquement appelée.
Dans la mémoire cache des mises à jour, les enregistrements supprimés sont
récupérés, les enregistrements modifiés retrouvent leurs valeurs initiales et les
enregistrements insérés disparaissent.
Remarque Cette option n’est pas disponible pour les ensembles de données client.

Annulation des mises à jour en mémoire cache en suspens


La méthode CancelUpdates efface toutes les mises à jour en suspens dans le cache
et remet l’ensemble de données dans l’état où il se trouvait lors de l’ouverture
de la table, de la dernière activation des mises à jour en mémoire cache ou lors
de l’application de la dernière mise à jour.
Par exemple, l’instruction suivante annule les mises à jour de la table
CustomersTable :
CustomersTable.CancelUpdates;
Dans la mémoire cache des mises à jour, les enregistrements supprimés sont
récupérés, les enregistrements modifiés retrouvent leurs valeurs initiales et les
enregistrements insérés disparaissent.

Manipulation des mises à jour en mémoire cache 25-9


Utilisation des mises à jour en mémoire cache

Remarque L’appel à CancelUpdates ne désactive pas les mises à jour en mémoire cache. Il ne
fait que désactiver les mises à jour en suspens. Pour désactiver les futures mises
à jour en mémoire cache, mettez la propriété CachedUpdates à False.

Annulation des mises à jour apportées à l’enregistrement en cours


La méthode RevertRecord restaure l’enregistrement en cours dans l’ensemble de
données à son état initial (c’est-à-dire l’état dans lequel il se trouvait à
l’ouverture de la table, lors de la dernière activation des mises à jour en
mémoire cache ou lors de la dernière application réussie des mises à jour). Elle
est fréquemment utilisée dans un gestionnaire d’événement OnUpdateError pour
corriger les situations d’erreurs. Exemple :
CustomersTable.RevertRecord;
L’annulation des modifications d’un enregistrement en mémoire cache n’affecte
pas les autres enregistrements. Si un seul enregistrement figure dans la mémoire
cache des mises à jour et que la modification est annulée avec RevertRecord, la
propriété UpdatesPending du composant ensemble de données passe
automatiquement de True à False.
Si l’enregistrement n’a pas été modifié, cet appel est sans effet. Pour plus
d’informations sur la création d’un gestionnaire OnUpdateErro, voir “Création
d’un gestionnaire d’événement OnUpdateRecord” à la page 25-26.

Récupération d’enregistrements en mémoire cache


L’annulation de la suppression d’un enregistrement placé en mémoire cache
nécessite du code, car une fois supprimé il n’est plus l’enregistrement en cours et
n’apparaît plus dans l’ensemble de données. Pour ce faire, il faut d’abord utiliser
la propriété UpdateRecordTypes pour rendre les enregistrements supprimés
visibles, puis faire appel à RevertRecord. Voici un exemple de code permettant
d’annuler la suppression de tous les enregistrements d’une table :
procedure TForm1.UndeleteAll(DataSet: TDataSet)
begin
DataSet.UpdateRecordTypes := [rtDeleted]; { n’affiche que les
enregistrements supprimés }
try
DataSet.First; { se positionne sur le premier enregistrement
précédemment supprimé }
while not (DataSet.Eof)
DataSet.RevertRecord; { récupère et répète l’opération jusqu’au
dernier enregistrement ]
except
{ restaure les types de mises à jour pour ne reconnaître que les
enregistrements modifiés, insérés et non modifiés }
DataSet.UpdateRecordTypes := [rtModified, rtInserted, rtUnmodified];
raise;
end;
DataSet.UpdateRecordTypes := [rtModified, rtInserted, rtUnmodified];
end;

25-10 Guide du développeur


Utilisation des mises à jour en mémoire cache

Spécification des enregistrements visibles en mémoire cache


La propriété UpdateRecordTypes contrôle le type des enregistrements visibles dans
la mémoire cache lorsque les mises à jour en mémoire cache sont activées.
UpdateRecordTypes fonctionne sur les enregistrements en mémoire cache de la
même façon que les filtres sur les tables. UpdateRecordTypes est un ensemble et
peut de ce fait contenir n’importe quelle combinaison des valeurs suivantes :

Tableau 25.1 Valeurs de TUpdateRecordType


Valeur Signification
rtModified Enregistrements modifiés.
rtInserted Enregistrements insérés.
rtDeleted Enregistrements supprimés.
rtUnmodified Enregistrements non modifiés.

Par défaut, la propriété UpdateRecordTypes ne contient que rtModified, rtInserted et


rtUnmodified, sans affichage des enregistrements supprimés (rtDeleted).
La propriété UpdateRecordTypes est particulièrement utile dans un gestionnaire
d’événement OnUpdateError pour accéder aux enregistrements supprimés afin
d’annuler leur suppression par un appel à RevertRecord. Cette propriété permet
aussi aux utilisateurs de votre application de ne visualiser qu’un sous-ensemble
d’enregistrements en mémoire cache, par exemple les enregistrements insérés
(rtInserted).
Par exemple, supposons un ensemble de quatre boutons radio (de RadioButton1 à
RadioButton4) avec les libellés All, Modified, Inserted et Deleted. Si le même
gestionnaire d’événement OnClick est affecté aux quatre boutons radio, vous
pouvez suivant les cas afficher tous les enregistrements (sauf ceux supprimés,
valeur par défaut), uniquement les enregistrements modifiés, uniquement les
enregistrements insérés ou uniquement les enregistrements supprimés en
définissant correctement la propriété UpdateRecordTypes.
procedure TForm1.UpdateFilterRadioButtonsClick(Sender: TObject);
begin
if RadioButton1.Checked then
CustomerQuery.UpdateRecordTypes := [rtUnmodified, rtModified, rtInserted]
else if RadioButton2.Checked then
CustomerQuery.UpdateRecordTypes := [rtModified]
else if RadioButton3.Checked then
CustomerQuery.UpdateRecordTypes := [rtInserted]
else
CustomerQuery.UpdateRecordTypes := [rtDeleted];
end;
Pour plus d’informations sur la création d’un gestionnaire OnUpdateError, voir
“Création d’un gestionnaire d’événement OnUpdateRecord” à la page 25-26.

Manipulation des mises à jour en mémoire cache 25-11


Utilisation des mises à jour en mémoire cache

Vérification de l’état de la mise à jour


Lorsque les mises à jour en mémoire cache sont activées pour une application,
vous pouvez faire le suivi de chaque enregistrement placé en mémoire cache en
examinant sa propriété UpdateStatus. La vérification des mises à jour est souvent
utilisée dans les gestionnaires d’événements OnUpdateRecord et OnUpdateError.
Pour plus d’informations sur la création et l’utilisation d’un événement
OnUpdateRecord, voir “Création d’un gestionnaire d’événement OnUpdateRecord”
à la page 25-26. Pour plus d’informations sur la création et l’utilisation d’un
événement OnUpdateError, voir “Gestion des erreurs de mise à jour en mémoire
cache” à la page 25-28.
Si vous effectuez des itérations dans un ensemble de modifications en suspens, la
valeur de la propriété UpdateStatus change pour refléter l’état de l’enregistrement
en cours. Elle renvoie l’une des valeurs suivantes pour l’enregistrement en cours :

Tableau 25.2 Valeurs de renvoi de la propriété UpdateStatus


Valeur Signification
usUnmodified Enregistrement inchangé.
usModified Enregistrement modifié.
usInserted Nouvel enregistrement.
usDeleted Enregistrement supprimé.

Lors de la première ouverture d’un ensemble de données, tous les


enregistrements sont dans l’état usUnmodified. A mesure que des enregistrements
sont insérés, supprimés et ainsi de suite, la valeur de leur état change. Dans
l’exemple suivant, la propriété UpdateStatus est utilisée dans un gestionnaire
d’événement OnScroll d’un ensemble de données. Le gestionnaire d’événement
affiche l’état de mise à jour de chaque enregistrement dans la barre d’état.
procedure TForm1.CustomerQueryAfterScroll(DataSet: TDataSet);
begin
with CustomerQuery do begin
case UpdateStatus of
usUnmodified: StatusBar1.Panels[0].Text := 'Unmodified';
usModified: StatusBar1.Panels[0].Text := 'Modified';
usInserted: StatusBar1.Panels[0].Text := 'Inserted';
usDeleted: StatusBar1.Panels[0].Text := 'Deleted';
else StatusBar1.Panels[0].Text := 'Undetermined status';
end;
end;
end;
Remarque Si la propriété UpdateStatus d’un enregistrement est à usModified, vous pouvez
examiner la propriété OldValue de chaque champ de l’ensemble de données pour
déterminer sa valeur précédente. OldValue est sans signification pour les
enregistrements qui ont des valeurs UpdateStatus différentes de usModified. Pour
plus d’informations sur la vérification et l’utilisation de la propriété OldValue,
voir “Accès aux propriétés OldValue, NewValue et CurValue d’un champ” à la
page 25-31.

25-12 Guide du développeur


Utilisation d’objets mise à jour pour mettre à jour un ensemble de données

Utilisation d’objets mise à jour pour mettre à jour un ensemble de


données
TUpdateSQL est un composant mise à jour utilisant des instructions SQL
préparées pour mettre à jour un ensemble de données. Vous devez fournir un
composant TUpdateSQL pour chaque table sous-jacente utilisée par la requête
originale.
Remarque Si vous utilisez plusieurs composants mise à jour pour effectuer l’opération de
mise à jour, vous devez créer un événement OnUpdateRecord pour exécuter
chaque composant mise à jour.
Un composant mise à jour encapsule trois composants TQuery, chacun d’eux se
chargeant d’une seule tâche à la fois. Un composant requête fournit une
instruction SQL UPDATE pour modifier les enregistrements d’une table ; un
deuxième fournit une instruction INSERT pour y ajouter des enregistrements ; un
troisième fournit une instruction DELETE pour en supprimer.
Lorsque vous placez un composant mise à jour dans un module de données,
vous ne pouvez pas voir les composants requête qu’il encapsule. Ceux-ci sont
créés à l’exécution par le composant mise à jour, en fonction des trois propriétés
de mise à jour pour lesquelles vous fournissez des instructions SQL :
• ModifySQL spécifie l’instruction UPDATE.
• InsertSQL spécifie l’instruction INSERT.
• DeleteSQL spécifie l’instruction DELETE.
A l’exécution, s’il est utilisé pour appliquer les mises à jour, le composant mise à
jour se comporte comme suit :
1 Il sélectionne une instruction SQL à exécuter en fonction du paramètre
UpdateKind automatiquement généré par un événement de mise à jour
d’enregistrement. UpdateKind spécifie si l’enregistrement en cours doit être
modifié, inséré ou supprimé.
2 Il fournit la valeur des paramètres de l’instruction SQL.
3 Il prépare et exécute l’instruction SQL pour qu’elle effectue la mise à jour
spécifiée.

Spécification de la propriété UpdateObject d’un ensemble de


données
Un ou plusieurs objets mise à jour peuvent être associés à un ensemble de
données à mettre à jour. Associez les objets mise à jour à l’ensemble de données
de mise à jour en attribuant à la propriété UpdateObject du composant ensemble
de données l’objet mise à jour ou en attribuant à la propriété DataSet de l’objet
mise à jour l’ensemble de données de mise à jour. Vous utilisez l’une ou l’autre
méthode selon qu’une ou plusieurs tables de base de l’ensemble de données de
mise à jour sont à mettre à jour.

Manipulation des mises à jour en mémoire cache 25-13


Utilisation d’objets mise à jour pour mettre à jour un ensemble de données

Vous devez utiliser l’une de ces deux méthodes d’association d’ensembles de


données de mise à jour à des objets mise à jour. Si l’association n’est pas
correcte, le remplissage dynamique des paramètres dans les instructions SQL de
l’objet mise à jour ne peut pas avoir lieu. Utilisez l’une ou l’autre des méthodes
d’association, mais jamais les deux.
L’association d’un objet mise à jour à un ensemble de données détermine aussi
l’exécution de cet objet. Un objet mise à jour peut être exécuté automatiquement,
sans intervention explicite de l’application, ou nécessairement de façon explicite.
Si l’association est réalisée à l’aide de la propriété UpdateObject du composant
ensemble de données, l’objet mise à jour est automatiquement exécuté. Si
l’association est réalisée à l’aide de la propriété DataSet de l’objet mise à jour,
vous devez coder l’exécution de l’objet mise à jour.
Les sections suivantes expliquent en détail le processus d’association d’objets
mise à jour à des composants ensemble de données de mise à jour, les situations
d’utilisation de chaque méthode et les conséquences sur l’exécution des mises à
jour.

Utilisation d’un seul objet mise à jour


Lorsqu’une seule des tables de base référencées dans l’ensemble de données de
mise à jour doit être mise à jour, associez un objet mise à jour à l’ensemble de
données en attribuant à la propriété UpdateObject du composant ensemble de
données le nom de l’objet mise à jour.
Query1.UpdateObject := UpdateSQL1;
Les instructions SQL de mise à jour contenues dans l’objet mise à jour sont
automatiquement exécutées lorsque la méthode ApplyUpdates de l’ensemble de
données de mise à jour est appelée. L’objet mise à jour est appelé pour chaque
enregistrement devant être mis à jour. N’appelez pas la méthode ExecSQL de
l’objet mise à jour dans un gestionnaire pour l’événement OnUpdateRecord car
cela aboutirait à une seconde tentative d’application de mise à jour pour chaque
enregistrement.
Si vous fournissez un gestionnaire pour l’événement OnUpdateRecord de
l’ensemble de données, l’action minimale que vous devez réaliser dans ce
gestionnaire consiste à attribuer au paramètre UpdateAction du gestionnaire
d’événement la valeur uaApplied. Vous pouvez, si vous le souhaitez, procéder à
la validation de données, à la modification de données ou à la réalisation
d’autres tâches comme la définition de valeurs de paramètre.

Utilisation de plusieurs objets mise à jour


Lorsque plusieurs tables de base référencées dans l’ensemble de données de mise
à jour doivent être mises à jour, vous devez utiliser plusieurs objets mise à jour,
à raison d’un par table de base mise à jour. Comme l’objet UpdateObject du
composant ensemble de données ne permet d’associer qu’un seul objet mise à
jour à l’ensemble de données, vous devez associer chaque objet mise à jour à
l’ensemble de données en attribuant à sa propriété DataSet le nom de l’ensemble

25-14 Guide du développeur


Utilisation d’objets mise à jour pour mettre à jour un ensemble de données

de données. La propriété DataSet des objets mise à jour n’est pas disponible lors
de conception dans l’inspecteur d’objets. Vous pouvez uniquement définir cette
propriété à l’exécution.
UpdateSQL1.DataSet := Query1;
Les instructions SQL de mise à jour contenues dans l’objet mise à jour ne sont
pas automatiquement exécutées lorsque la méthode ApplyUpdates de l’ensemble
de données de mise à jour est appelée. Pour mettre à jour les enregistrements,
vous devez fournir un gestionnaire pour l’événement OnUpdateRecord du
composant ensemble de données et appeler la méthode ExecSQL ou Apply de
l’objet mise à jour. Cela appelle l’objet mise à jour pour chaque enregistrement
qui doit être mis à jour.
Dans le gestionnaire d’événement OnUpdateRecord de l’ensemble de données, les
actions minimales que vous devez réaliser sont les suivantes :
• Appeler la méthode SetParams de l’objet mise à jour (si vous appelez
ultérieurement ExecSQL).
• Exécuter l’objet mise à jour pour l’enregistrement en cours avec ExecSQL ou
Apply.
• Attribuer au paramètre UpdateAction du gestionnaire d’événement la valeur
uaApplied.
Vous pouvez, si vous le souhaitez, procéder à la validation de données, à la
modification de données ou à la réalisation d’autres tâches liées à la mise à jour
de chaque enregistrement.
Remarque Vous pouvez aussi associer un objet mise à jour à l’ensemble de données à l’aide
de la propriété UpdateObject du composant ensemble de données et les suivants à
l’aide de leur propriété DataSet. Le premier objet mise à jour est
automatiquement exécuté lors de l’appel de la méthode ApplyUpdates du
composant ensemble de données. Les autres doivent être exécutés manuellement.

Création d’instructions SQL pour les composants mise à jour


Pour mettre à jour un enregistrement dans un ensemble de données associé, un
objet mise à jour utilise trois instructions SQL, permettant respectivement de
supprimer, insérer et modifier les enregistrements mis en mémoire cache en vue
d’une mise à jour. Les instructions figurent dans les propriétés liste de chaînes
DeleteSQL, InsertSQL et ModifySQL de l’objet mise à jour. Comme chaque objet
mise à jour permet de mettre à jour une seule table, les instructions de mise à
jour de chaque objet font référence à la même table de base.
Lors de l’application de la mise à jour de chaque enregistrement, l’une des trois
instructions SQL est exécutée sur la table de base mise à jour. L’exécution de
telle ou telle instruction SQL dépend du paramètre UpdateKind automatiquement
généré pour la mise à jour de chaque enregistrement.
Les instructions SQL pour les objets mise à jour peuvent être créées lors de la
conception ou à l’exécution. Les sections suivantes décrivent en détail la création
d’instructions SQL de mise à jour.

Manipulation des mises à jour en mémoire cache 25-15


Utilisation d’objets mise à jour pour mettre à jour un ensemble de données

Création d’instructions SQL lors de la conception


Pour créer les instructions SQL d’un composant mise à jour,
1 Sélectionnez le composant TUpdateSQL.
2 Sélectionnez le nom du composant mise à jour dans la liste déroulante de la
propriété UpdateObject du composant ensemble de données dans l’inspecteur
d’objets. Ainsi l’éditeur UpdateSQL que vous lancez à l’étape suivante peut
déterminer les valeurs par défaut les mieux adaptées aux options de
génération SQL.
3 Cliquez avec le bouton droit sur le composant mise à jour et sélectionnez
Editeur UpdateSQL dans le menu contextuel pour lancer l’éditeur de mise à
jour SQL (aussi appelé éditeur UpdateSQL). Celui-ci crée alors les instructions
SQL nécessaires aux propriétés ModifySQL, InsertSQL et DeleteSQL du
composant mise à jour d’après l’ensemble de données sous-jacent et les
valeurs que vous lui fournissez.
L’éditeur UpdateSQL comporte deux pages. La page Options apparaît lorsque
vous faites appel à l’éditeur pour la première fois. Pour sélectionner la table à
mettre à jour, utilisez la boîte à options Nom de table. Quand vous spécifiez un
nom de table, les boîtes liste “Champs clé” et “Champs mise à jour” affichent les
colonnes disponibles.
La boîte liste “Champs mise à jour” indique quels sont les champs à mettre à
jour. Lorsque vous spécifiez une table pour la première fois, tous les champs de
cette boîte liste sont sélectionnés. Si vous le souhaitez, vous pouvez sélectionner
plusieurs champs à la fois.
La boîte liste Champs clé sert à spécifier les champs à utiliser comme clés lors de
la mise à jour. Pour les tables Paradox, dBASE et FoxPro, les champs spécifiés
doivent correspondre à un index existant (ce n’est pas obligatoire pour les bases
de données SQL distantes). Au lieu de définir les champs clés, vous pouvez
cliquer sur le bouton Sélection clés primaires pour choisir les champs clés de la
mise à jour en fonction de l’index primaire de la table. Cliquez sur Défaut pour
DataSet pour restituer aux listes de sélection leur état original : cela concerne
tous les champs sélectionnés en tant que clés et tous ceux sélectionnés en vue
d’une mise à jour.
Activez la case à cocher “Noms de champs entre guillemets” si votre serveur
requiert l’utilisation de guillemets pour les noms de champs.
Après avoir spécifié une table, sélectionné les colonnes clé et les champs à mettre
à jour, cliquez sur “Générer SQL” pour générer les instructions SQL
préliminaires à associer aux propriétés ModifySQL, InsertSQL et DeleteSQL du
composant mise à jour. Dans la plupart des cas, il est nécessaire de préciser et
d’affiner les instructions SQL générées.
Pour afficher et modifier les instructions SQL générées, sélectionnez la page SQL.
Si vous avez demandé la génération des instructions SQL, l’instruction de la
propriété ModifySQL est déjà affichée dans la boîte “Texte SQL” quand vous
sélectionnez cette page. Vous pouvez alors éditer l’instruction.

25-16 Guide du développeur


Utilisation d’objets mise à jour pour mettre à jour un ensemble de données

Important N’oubliez pas que les instructions SQL générées ne sont qu’un point de départ
pour créer les instructions de mise à jour. Il est parfois nécessaire de les modifier
pour qu’elles s’exécutent correctement. Ainsi, pour travailler avec des données
contenant des valeurs NULL, il peut être nécessaire de modifier la clause
WHERE pour obtenir :
WHERE field IS NULL
au lieu d’utiliser la variable champ générée. Testez chaque instruction générée
avant de la valider.
Pour passer d’un type d’instruction SQL généré à un autre et l’éditer, utilisez les
boutons radio Type d’instruction.
Pour accepter les instructions et les associer aux propriétés SQL du composant
mise à jour, cliquez sur OK.

Substitution des paramètres dans les instructions SQL de mise à jour


Les instructions SQL de mise à jour utilisent une forme particulière de
substitution de paramètres permettant de substituer des valeurs de champs
anciennes ou nouvelles dans les mises à jour d’enregistrement. Lorsque l’éditeur
UpdateSQL génère les instructions, il détermine quelles sont les valeurs de
champ à utiliser. Lorsque vous écrivez le code SQL de mise à jour, vous
spécifiez les valeurs de champ à utiliser.
Quand le nom du paramètre correspond à un nom de champ, la nouvelle valeur
contenue dans ce champ dans la mise à jour en mémoire cache de
l’enregistrement est automatiquement utilisée comme valeur pour le paramètre.
Quand il correspond à un nom de champ précédé de la chaîne “OLD_”, c’est
l’ancienne valeur du champ qui est prise en compte. Par exemple, dans
l’instruction SQL de mise à jour suivante, le paramètre :LastName est
automatiquement spécifié pour l’enregistrement inséré à partir de la nouvelle
valeur de champ contenue dans la mise à jour en mémoire cache.
INSERT INTO Names
(LastName, FirstName, Address, City, State, Zip)
VALUES (:LastName, :FirstName, :Address, :City, :State, :Zip)
Les nouvelles valeurs de champs sont généralement utilisées dans les instructions
InsertSQL et ModifySQL. Dans la mise à jour d’un enregistrement modifié, la
nouvelle valeur de champ provenant de la mémoire cache de mise à jour est
utilisée par l’instruction UPDATE pour remplacer l’ancienne valeur de champ
dans la table de base mise à jour.
Dans le cas d’un enregistrement supprimé, il n’y a pas de nouvelle valeur ; de ce
fait, la propriété DeleteSQL utilise la syntaxe “:OLD_NomChamp”. Généralement,
les anciennes valeurs de champ sont également utilisées avec la clause WHERE
de l’instruction SQL pour une mise à jour par modification ou suppression pour
déterminer quel est l’enregistrement à mettre à jour ou à supprimer.

Manipulation des mises à jour en mémoire cache 25-17


Utilisation d’objets mise à jour pour mettre à jour un ensemble de données

Dans la clause WHERE d’une instruction SQL de mise à jour UPDATE ou


DELETE, fournissez au moins le nombre minimal de paramètres permettant
d’identifier de façon univoque l’enregistrement dans la table de base qui est mis
à jour à partir des données en mémoire cache. Par exemple, dans une liste de
clients, l’utilisation du nom du client uniquement peut ne pas être suffisante
pour identifier de façon univoque l’enregistrement adéquat dans la table de
base ; le nom “Dupont” peut figurer dans plusieurs enregistrements. Par contre,
la combinaison de paramètres pour le nom, le prénom et le numéro de téléphone
peut être suffisante. L’idéal est de disposer d’une valeur de champ unique,
comme le numéro de client.
Pour en savoir plus sur la substitution entre les valeurs anciennes et nouvelles
des paramètres, reportez-vous à “Accès aux propriétés OldValue, NewValue et
CurValue d’un champ” à la page 25-31.

Elaboration des instructions SQL de mise à jour


Le composant TUpdateSQL possède trois propriétés pour les instructions SQL de
mise à jour : DeleteSQL, InsertSQL et ModifySQL. Comme le nom des propriétés
l’indique, ces instructions SQL suppriment, insèrent et modifient les
enregistrements dans la table de base.
La propriété DeleteSQL doit contenir uniquement une instruction SQL avec la
commande DELETE. La table de base à mettre à jour doit être nommée dans la
clause FROM. Pour que l’instruction SQL ne supprime dans la table de base que
l’enregistrement correspondant à celui supprimé dans la mémoire cache de mise
à jour, utilisez une clause WHERE. Dans la clause WHERE, utilisez un paramètre
pour un ou plusieurs champs afin d’identifier de façon univoque
l’enregistrement dans la table de base correspondant à celui figurant dans la
mémoire cache de mise à jour. Si les paramètres sont nommés comme les
champs et précédés de “OLD_”, ils reçoivent automatiquement les valeurs des
champs correspondants de l’enregistrement figurant dans la mémoire cache de
mise à jour. Si les paramètres sont nommés d’une autre façon, vous devez
fournir les valeurs de paramètre.
DELETE FROM Inventory I
WHERE (I.ItemNo = :OLD_ItemNo)
Certains types de tables peuvent ne pas trouver l’enregistrement dans la table de
base si des champs utilisés pour identifier l’enregistrement contiennent des
valeurs NULL. Dans ces cas, la mise à jour par suppression échoue pour ces
enregistrements. Pour y remédier, ajoutez une condition pour les champs
pouvant contenir une valeur NULL à l’aide du prédicat IS NULL (outre une
condition pour une valeur non NULL). Par exemple, lorsqu’un champ FirstName
est susceptible de contenir une valeur NULL, vous pouvez utiliser le code
suivant :
DELETE FROM Names
WHERE (LastName = :OLD_LastName) AND
((FirstName = :OLD_FirstName) OR (FirstName IS NULL))

25-18 Guide du développeur


Utilisation d’objets mise à jour pour mettre à jour un ensemble de données

L’instruction InsertSQL ne doit contenir qu’une instruction SQL avec la


commande INSERT. La table de base à mettre à jour doit être nommée dans la
clause INTO. Dans la clause VALUES, fournissez une liste de paramètres séparés
par des virgules. Si les paramètres sont nommés comme les champs, ils reçoivent
automatiquement les valeurs des champs correspondants de l’enregistrement
figurant dans la mémoire cache de mise à jour. Si les paramètres sont nommés
d’une autre façon, vous devez fournir les valeurs de paramètre. La liste de
paramètres fournit les valeurs des champs du nouvel enregistrement inséré. Il
doit y avoir autant de paramètres de valeur que de champs listés dans
l’instruction.
INSERT INTO Inventory
(ItemNo, Amount)
VALUES (:ItemNo, 0)
L’instruction ModifySQL ne doit contenir qu’une instruction SQL avec la
commande UPDATE. La table de base à mettre à jour doit être nommée dans la
clause FROM. Incluez une ou plusieurs affectations de valeur dans la clause SET.
Si les valeurs affectées dans la clause SET sont des paramètres nommés comme
les champs, les paramètres reçoivent automatiquement les valeurs des champs
correspondants de l’enregistrement mis à jour figurant dans la mémoire cache.
Vous pouvez affecter d’autres valeurs de champ utilisant d’autres paramètres,
sous réserve que ces paramètres ne portent pas le nom d’un champ et que vous
fournissiez les valeurs manuellement. Comme pour l’instruction DeleteSQL,
fournissez une clause WHERE, pour identifier de façon univoque
l’enregistrement dans la table de base à mettre à jour, à l’aide de paramètres
nommés comme les champs et précédés de “OLD_”. Dans l’instruction de mise à
jour suivante, le paramètre :ItemNo reçoit automatiquement une valeur,
contrairement à :Price.
UPDATE Inventory I
SET I.ItemNo = :ItemNo, Amount = :Price
WHERE (I.ItemNo = :OLD_ItemNo)
A partir du code SQL de mise à jour ci-dessus, prenons l’exemple d’une
application dans laquelle l’utilisateur final modifie un enregistrement existant. La
valeur d’origine du champ ItemNo est 999. Dans une grille connectée à
l’ensemble de données figurant en mémoire cache, l’utilisateur final modifie la
valeur du champ ItemNo en 123 et celle du champ Amount en 20. Lorsque la
méthode ApplyUpdates est appelée, cette instruction SQL affecte tous les
enregistrements dans la table de base dont la valeur du champ ItemNo est 999, à
partir de l’ancienne valeur contenue dans le paramètre :OLD_ItemNo. Dans ces
enregistrements, l’instruction modifie la valeur du champ ItemNo en 123 (valeur
provenant du paramètre :ItemNo de la grille) et celle du champ Amount en 20.

Utilisation de la propriété Query d’un composant mise à jour


Utilisez la propriété Query d’un composant mise à jour pour accéder à l’une des
propriétés SQL de mise à jour (DeleteSQL, InsertSQL ou ModifySQL), par exemple
pour définir ou modifier l’instruction SQL. Utilisez les valeurs de constante
UpdateKind comme index du tableau des composants requête. La propriété Query
n’est accessible qu’à l’exécution.

Manipulation des mises à jour en mémoire cache 25-19


Utilisation d’objets mise à jour pour mettre à jour un ensemble de données

L’instruction suivante utilise la constante UpdateKind ukDelete avec la propriété


Query pour accéder à la propriété DeleteSQL.
with UpdateSQL1.Query[ukDelete] do begin
Clear;
Add(‘DELETE FROM Inventory I’);
Add(‘WHERE (I.ItemNo = :OLD_ItemNo)’);
end;
Normalement, les propriétés indexées par la propriété Query sont définies lors de
la conception à l’aide de l’éditeur de mise à jour SQL. Il se peut, toutefois, que
vous deviez accéder à ces valeurs à l’exécution si vous générez une instruction
SQL de mise à jour par enregistrement et n’utilisez pas la liaison de paramètres.
L’exemple suivant génère une valeur unique pour la propriété Query pour
chaque ligne mise à jour :
procedure TForm1.EmpAuditUpdateRecord(DataSet: TDataSet;
UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
begin
with UpdateSQL1 do begin
case UpdateKind of
ukModified:
begin
Query[UpdateKind].Text := Format('update emptab set Salary = %d where EmpNo =
%d',
[EmpAuditSalary.NewValue, EmpAuditEmpNo.OldValue]);
ExecSQL(UpdateKind);
end;
ukInserted:
{...}
ukDeleted:
{...}
end;
end;
UpdateAction := uaApplied;
end;
Remarque Query renvoie une valeur de type TDataSetUpdateObject. Pour traiter cette valeur
de retour comme un composant TUpdateSQL, afin d’utiliser les propriétés et les
méthodes propres à TUpdateSQL, transtypez la propriété UpdateObject. Par
exemple :
with (DataSet.UpdateObject as TUpdateSQL).Query[UpdateKind] do begin
{ réalise les opérations sur l’instruction dans DeleteSQL }
end;
Pour un exemple d’utilisation de cette propriété, voir “Appel de la méthode
SetParams” à la page 25-22.

Utilisation des propriétés DeleteSQL, InsertSQL et ModifySQL


Utilisez les propriétés DeleteSQL, InsertSQL et ModifySQL pour définir les
instructions SQL de mise à jour correspondantes. Ces propriétés sont toutes des
conteneurs de liste de chaînes. Utilisez les méthodes de listes de chaînes pour
entrer les lignes d’instructions SQL en tant qu’éléments dans ces propriétés.

25-20 Guide du développeur


Utilisation d’objets mise à jour pour mettre à jour un ensemble de données

Utilisez une valeur entière comme index pour référencer une ligne donnée dans
la propriété. Les propriétés DeleteSQL, InsertSQL et ModifySQL sont accessibles
lors de la conception et à l’exécution.
with UpdateSQL1.DeleteSQL do begin
Clear;
Add(‘DELETE FROM Inventory I’);
Add(‘WHERE (I.ItemNo = :OLD_ItemNo)’);
end;
Ci-après, la troisième ligne d’une instruction SQL est modifiée à l’aide d’un
index de valeur 2 pour la propriété ModifySQL.
UpdateSQL1.ModifySQL[2] := ‘WHERE ItemNo = :ItemNo’;

Exécution des instructions de mise à jour


Différentes méthodes permettent d’exécuter le code SQL de mise à jour d’un
enregistrement. Les appels de méthode sont généralement utilisés dans le
gestionnaire d’événement OnUpdateRecord de l’objet mise à jour pour exécuter le
code SQL permettant d’appliquer la mise à jour à l’enregistrement en cours placé
en mémoire cache. Les différentes méthodes s’appliquent dans des circonstances
différentes. Les sections suivantes présentent chacune de ces méthodes en détail.

Appel de la méthode Apply


La méthode Apply d’un composant mise à jour a pour effet d’appliquer
manuellement les mises à jour relatives à l’enregistrement en cours. Ce processus
comporte deux étapes :
1 les valeurs de l’enregistrement sont liées aux paramètres de l’instruction SQL
de mise à jour appropriée.
2 l’instruction SQL est exécutée.
Appelez la méthode Apply pour appliquer la mise à jour de l’enregistrement en
cours figurant dans la mémoire cache de mise à jour. Utilisez uniquement Apply
lorsque l’objet mise à jour n’est pas associé à l’ensemble de données à l’aide de
la propriété UpdateObject du composant ensemble de données, auquel cas l’objet
mise à jour n’est pas automatiquement exécuté. Apply appelle automatiquement
la méthode SetParams pour lier les anciennes et nouvelles valeurs de champs aux
paramètres nommés dans l’instruction SQL de mise à jour. N’appelez pas
SetParams vous-même lorsque vous utilisez Apply. La méthode Apply est le plus
souvent appelée à partir du gestionnaire d’événement OnUpdateRecord de
l’ensemble de données.
Si vous utilisez la propriété UpdateObject du composant ensemble de données
pour associer l’ensemble de données et l’objet mise à jour, cette méthode est
automatiquement appelée. N’appelez pas Apply dans le gestionnaire d’événement
OnUpdateRecord du composant ensemble de données car cela aboutirait à une
seconde tentative d’application de mise à jour pour l’enregistrement en cours.

Manipulation des mises à jour en mémoire cache 25-21


Utilisation d’objets mise à jour pour mettre à jour un ensemble de données

Dans le gestionnaire d'événement OnUpdateRecord, le paramètre UpdateKind


permet de déterminer l'instruction SQL de mise à jour à utiliser. S'il est appelé
par l'ensemble de données associé, le paramètre UpdateKind est initialisé
automatiquement. Si vous faites appel à cette méthode dans un événement
OnUpdateRecord, transmettez une constante UpdateKind en tant que paramètre
d’Apply.
procedure TForm1.EmpAuditUpdateRecord(DataSet: TDataSet;
UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
begin
UpdateSQL1.Apply(UpdateKind);
UpdateAction := uaApplied;
end;
Si une exception est provoquée au cours de l’exécution du programme de mise à
jour, l’exécution se poursuit dans l’événement OnUpdateError, s’il a été défini.
Remarque Les opérations effectuées par Apply sont analogues aux méthodes SetParams et
ExecSQL décrites dans les sections qui suivent.

Appel de la méthode SetParams


La méthode SetParams d’un composant mise à jour utilise des règles de
substitution des paramètres pour substituer des anciennes et nouvelles valeurs de
champs dans une instruction SQL de mise à jour. Généralement, SetParams est
appelée automatiquement par la méthode Apply du composant mise à jour. Si
vous faites appel à Apply directement dans un événement OnUpdateRecord, vous
n’avez pas à appeler SetParams. Si vous exécutez un objet mise à jour à l'aide de
sa méthode ExecSQL, appelez SetParams pour lier les valeurs aux paramètres de
l'instruction de mise à jour.
SetParams initialise les paramètres de l’instruction SQL spécifiée par le paramètre
UpdateKind. Une valeur est automatiquement et uniquement affectée aux
paramètres qui utilisent une convention d'appellation spéciale. Si le paramètre
porte le même nom qu'un champ, précédé ou non de “OLD_”, il est
automatiquement une valeur. Les paramètres nommés autrement doivent
recevoir une valeur manuellement. Pour plus d'informations, voir la section
“Substitution des paramètres dans les instructions SQL de mise à jour” à la
page 25-17.
L’exemple suivant illustre l’utilisation de SetParams :
procedure TForm1.EmpAuditUpdateRecord(DataSet: TDataSet;
UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
begin
with DataSet.UpdateObject as TUpdateSQL do begin
SetParams(UpdateKind);
if UpdateKind = ukModified then
Query[UpdateKind].ParamByName('DateChanged').Value := Now;
ExecSQL(UpdateKind);
end;
UpdateAction := uaApplied;
end;

25-22 Guide du développeur


Utilisation d’objets mise à jour pour mettre à jour un ensemble de données

Remarque Cet exemple part du principe que la propriété ModifySQL du composant mise à
jour se présente comme suit :
UPDATE EmpAudit
SET EmpNo = :EmpNo, Salary = :Salary, Changed = :DateChanged
WHERE EmpNo = :OLD_EmpNo
Dans cet exemple, l’appel à SetParams fournit des valeurs aux paramètres EmpNo
et Salary. Le paramètre DateChanged n’est pas défini parce que son nom ne
correspond pas à celui d’un champ de l’ensemble de données ; de ce fait, la ligne
de code suivante définit cette valeur explicitement.

Appel de la méthode ExecSQL


La méthode ExecSQL d’un composant mise à jour a pour effet d’appliquer
manuellement les mises à jour relatives à l’enregistrement en cours. Ce processus
comporte deux étapes :
1 les valeurs de l’enregistrement sont liées aux paramètres de l’instruction SQL
de mise à jour appropriée.
2 l’instruction SQL est exécutée.
Appelez la méthode ExecSQL pour appliquer la mise à jour de l’enregistrement
en cours figurant dans la mémoire cache de mise à jour. Utilisez uniquement
ExecSQL lorsque l’objet mise à jour n’est pas associé à l’ensemble de données à
l’aide de la propriété UpdateObject du composant ensemble de données, auquel
cas l’objet mise à jour n’est pas automatiquement exécuté. ExecSQL n’appelle pas
automatiquement la méthode SetParams pour lier les valeurs de paramètres de
l’instruction SQL de mise à jour. Appelez SetParams vous-même avant d’appeler
ExecSQL. La méthode ExecSQL est le plus souvent appelée à partir du
gestionnaire d’événement OnUpdateRecord de l’ensemble de données.
Si vous utilisez la propriété UpdateObject du composant ensemble de données
pour associer l’ensemble de données et l’objet mise à jour, cette méthode est
automatiquement appelée. N’appelez pas ExecSQL dans le gestionnaire
d’événement OnUpdateRecord du composant ensemble de données car cela
aboutirait à une seconde tentative d’application de mise à jour pour
l’enregistrement en cours.
Dans le gestionnaire d’événement OnUpdateRecord, le paramètre UpdateKind
permet de déterminer l’instruction SQL de mise à jour à utiliser. S’il est appelé par
l’ensemble de données associé, le paramètre UpdateKind est initialisé automatique-
ment. Si vous faites appel à cette méthode dans un événement OnUpdateRecord,
transmettez une constante UpdateKind en tant que paramètre de ExecSQL.
procedure TForm1.EmpAuditUpdateRecord(DataSet: TDataSet;
UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
begin
with (DataSet.UpdateObject as TUpdateSQL) do begin
SetParams(UpdateKind);
ExecSQL(UpdateKind);
end;
UpdateAction := uaApplied;
end;

Manipulation des mises à jour en mémoire cache 25-23


Utilisation d’objets mise à jour pour mettre à jour un ensemble de données

Si une exception est provoquée au cours de l’exécution du programme de mise à


jour, l’exécution se poursuit dans l’événement OnUpdateError, s’il a été défini.
Remarque Les opérations effectuées par ExecSQL et SetParams sont analogues à la méthode
Apply décrite précédemment.

Utilisation de composants ensemble de données pour mettre à


jour un ensemble de données
L’application des mises à jour en mémoire cache implique généralement
l’utilisation d’un ou plusieurs objets mise à jour. Les instructions SQL de mise à
jour de ces objets appliquent les modifications de données à la table de base.
L’utilisation de composants mise à jour est la façon la plus facile de mettre à
jour un ensemble de données mais elle n’est pas obligatoire. Vous pouvez aussi
utiliser des composants ensemble de données comme TTable et TQuery pour
appliquer les mises à jour en mémoire cache.
Dans le gestionnaire d’événement OnUpdateRecord du composant ensemble de
données, utilisez les propriétés et les méthodes d’un autre composant ensemble
de données pour appliquer les mises à jour en mémoire cache pour chaque
enregistrement.
Par exemple, le code suivant utilise un composant table pour réaliser les mises à
jour :
procedure TForm1.EmpAuditUpdateRecord(DataSet: TDataSet;
UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
begin
if UpdateKind = ukInsert then
UpdateTable.AppendRecord([DataSet.Fields[0].NewValue, DataSet.Fields[1].NewValue])
else
if UpdateTable.Locate('KeyField', VarToStr(DataSet.Fields[1].OldValue), []) then
case UpdateKind of
ukModify:
begin
Edit;
UpdateTable.Fields[1].AsString := VarToStr(DataSet.Fields[1].NewValue);
Post;
end;
ukInsert:
begin
Insert;
UpdateTable.Fields[1].AsString := VarToStr(DataSet.Fields[1].NewValue);
Post;
end;
ukModify: DeleteRecord;
end;
UpdateAction := uaApplied;
end;

25-24 Guide du développeur


Mise à jour d’un ensemble de résultat en lecture seule

Mise à jour d’un ensemble de résultat en lecture seule


Lorsque la propriété RequestLive d’un composant TQuery est à True, le moteur de
bases de données Borland (BDE) essaie de fournir un résultat de requête qu’il est
possible de mettre à jour (aussi appelé résultat “modifiable”). Toutefois, cette
opération est impossible dans certaines situations. Pour plus d’informations, voir
la section “Quand utiliser les mises à jour en mémoire cache ?” à la page 25-1.
Dans ces situations, vous pouvez mettre à jour manuellement un ensemble de
données comme suit :
1 Ajoutez un composant TUpdateSQL au module de données de votre
application.
2 Donnez à la propriété UpdateObject du composant ensemble de données le
nom du composant TUpdateSQL du module de données.
3 Entrez l’instruction SQL de mise à jour de l’ensemble de résultats dans la
propriété ModifySQL, InsertSQL ou DeleteSQL du composant mise à jour, ou
bien utilisez l’éditeur UpdateSQL.
4 Fermez l’ensemble de données.
5 Mettez la propriété CachedUpdates du composant ensemble de données à True.
6 Ouvrez à nouveau l’ensemble de données.
Remarque Dans de nombreuses circonstances, il est nécessaire d’écrire aussi un gestionnaire
d’événement OnUpdateRecord pour l’ensemble de données.

Contrôle du processus de mise à jour


L’appel de la méthode ApplyUpdates d’un composant ensemble de données
génère une tentative d’application de mise à jour des enregistrements de la table
de base par tous les enregistrements correspondants de la mémoire cache de
mise à jour. Chaque fois qu’une mise à jour pour un enregistrement modifié,
supprimé ou nouvellement inséré est sur le point d’être appliquée, l’événement
OnUpdateRecord du composant ensemble de données est déclenché.
Si vous fournissez un gestionnaire d’événement OnUpdateRecord, vous pouvez
réaliser des actions avant l’application effective de la mise à jour de
l’enregistrement en cours. Ces actions peuvent inclure la validation de données
particulières, la mise à jour d’autres tables ou l’exécution de plusieurs objets
mise à jour. Un gestionnaire d’événement OnUpdateRecord vous permet de mieux
contrôler le processus de mise à jour.
Les sections suivantes expliquent à quels moments vous devez fournir un
gestionnaire d’événement OnUpdateRecord et comment le créer.

Manipulation des mises à jour en mémoire cache 25-25


Contrôle du processus de mise à jour

Détermination de la nécessité de contrôler le processus de mise à


jour
Lorsqu’on utilise les mises à jour en mémoire cache, il suffit parfois d’appeler
ApplyUpdates pour appliquer les modifications dans les tables de base de la base
de données (par exemple, lorsque vous avez un accès exclusif à une table locale
Paradox ou dBASE par l’intermédiaire d’un composant TTable). Dans les autres
cas, il est nécessaire de prévoir des traitements supplémentaires pour garantir
l’application des mises à jour. Utilisez un gestionnaire d’événement
OnUpdateRecord du composant ensemble de données mis à jour pour fournir ces
traitements supplémentaires.
Par exemple, l’événement OnUpdateRecord peut être utilisé pour fournir des
routines de validation qui ajustent les données avant qu’elles ne soient
appliquées à la table, ou bien pour fournir un traitement supplémentaire pour
les enregistrements des tables maître et détail avant leur écriture dans les tables
de base.
Dans de nombreuses situations, vous devrez fournir un traitement
supplémentaire. Par exemple, si vous accédez à plusieurs tables en utilisant une
jointure, vous devez fournir un objet TUpdateSQL pour chaque table de la
requête et utiliser l’événement OnUpdateRecord pour garantir que chaque objet
mise à jour est exécuté pour écrire les modifications dans les tables.
Les sections suivantes décrivent comment créer et utiliser un objet TUpdateSQL et
un événement OnUpdateRecord.

Création d’un gestionnaire d’événement OnUpdateRecord


Le gestionnaire d’événement OnUpdateRecord peut être employé lorsqu’un même
composant mise à jour ne peut être utilisé pour les mises à jour requises, ou bien
si votre application a besoin de mieux contrôler la substitution de paramètres
spéciaux. L’événement OnUpdateRecord est déclenché une fois pour appliquer les
mises à jour à chaque enregistrement figurant dans la mémoire cache de mise à
jour.
Pour créer un gestionnaire d’événement OnUpdateRecord pour un ensemble de
données :
1 Sélectionnez le composant ensemble de données.
2 Choisissez la page Evénements dans l’inspecteur d’objets.
3 Double-cliquez sur la valeur de l’événement OnUpdateRecord pour lancer
l’éditeur de code.
Voici, schématiquement, le code d’un gestionnaire d’événement OnUpdateRecord :
procedure TForm1.DataSetUpdateRecord(DataSet: TDataSet;
UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
begin
{ Traitement des mises à jour... }
end;

25-26 Guide du développeur


Contrôle du processus de mise à jour

Le paramètre DataSet spécifie l’ensemble de données en mémoire cache contenant


les mises à jour.
Le paramètre UpdateKind indique le type de mise à jour à effectuer. Les valeurs
possibles pour UpdateKind sont ukModify, ukInsert et ukDelete. Quand vous utilisez
un composant mise à jour, il peut être nécessaire de transmettre ce paramètre à
ses méthodes d’exécution et de liaison de paramètres. Par exemple, l’utilisation
de ukModify avec la méthode Apply exécute l’instruction ModifySQL de l’objet
mise à jour. Vous devrez peut-être inspecter ce paramètre si votre gestionnaire
doit effectuer des traitements particuliers en fonction du type de mise à jour.
Le paramètre UpdateAction indique si vous avez appliqué une mise à jour ou
non. Les valeurs possibles pour UpdateAction sont uaFail (valeur par défaut),
uaAbort, uaSkip, uaRetry et uaApplied. Sauf si vous rencontrez un problème lors de
la mise à jour, votre gestionnaire d’événement doit en principe mettre ce
paramètre à uaApplied avant la sortie. Si vous décidez de ne pas mettre à jour un
enregistrement particulier, définissez la valeur à uaSkip pour garder les
changements non appliqués dans le cache.
Si vous ne changez pas la valeur du paramètre UpdateAction, toute l’opération de
mise à jour de l’ensemble de données est annulée. Pour en savoir plus sur
UpdateAction, reportez-vous à “Spécification de l’action à entreprendre” à la
page 25-29.
Outre ces paramètres, vous aurez besoin en général d’utiliser les propriétés
OldValue et NewValue du composant champ associé à l’enregistrement en cours.
Pour plus d’informations sur les propriétés OldValue et NewValue, voir “Accès
aux propriétés OldValue, NewValue et CurValue d’un champ” à la page 25-31.
Important L’événement OnUpdateRecord, à l’instar des gestionnaires d’événements
OnUpdateError et OnCalcFields, ne doit en principe jamais faire appel à des méthodes
susceptibles de changer l’enregistrement en cours d’un ensemble de données.
Voici un gestionnaire d’événement OnUpdateRecord qui exécute deux composants
mise à jour à l’aide de leur méthode Apply. Le paramètre UpdateKind est transmis
à la méthode Apply pour déterminer l’instruction SQL de mise à jour à exécuter
dans chaque objet mise à jour.
procedure TForm1.EmpAuditUpdateRecord(DataSet: TDataSet;
UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
begin
EmployeeUpdateSQL.Apply(UpdateKind);
JobUpdateSQL.Apply(UpdateKind);
UpdateAction := uaApplied;
end;
Dans cet exemple, le paramètre DataSet n’est pas utilisé. En effet, les composants
mise à jour ne sont pas associés au composant ensemble de données à l’aide de
sa propriété UpdateObject.

Manipulation des mises à jour en mémoire cache 25-27


Gestion des erreurs de mise à jour en mémoire cache

Gestion des erreurs de mise à jour en mémoire cache


Etant donné qu’il y a un certain délai entre le moment où un enregistrement est
mis en mémoire cache et celui où les mises à jour en mémoire cache sont
appliquées, il est possible qu’une autre application modifie l’enregistrement dans
la base de données avant que vous n’appliquiez la mise à jour. Même s’il n’y a pas
de conflit entre les mises à jour utilisateur, des erreurs peuvent se produire lors de
l’application de la mise à jour d’un enregistrement. Le BDE (moteur de base de
données Borland) vérifie, entre autres situations, la présence de conflits de mise à
jour utilisateur lors de la tentative de mise à jour et signale l’erreur survenue.
Le gestionnaire d’événement OnUpdateError d’un composant ensemble de
données vous permet d’intercepter les erreurs et d’y répondre. Si vous utilisez
des mises à jour en mémoire cache, il est préférable de créer un gestionnaire
pour cet événement. Si vous omettez de le faire et si une erreur se produit, toute
l’opération de mise à jour échouera.
Attention Ne faites pas appel à des méthodes susceptibles de changer l’enregistrement en
cours (comme Next ou Prior) dans un gestionnaire d’événement OnUpdateError.
En procédant ainsi, votre gestionnaire d’événement risquerait de se retrouver
pris dans une boucle sans fin.
Voici, schématiquement, le code d’un gestionnaire d’événement OnUpdateError :
procedure TForm1.DataSetUpdateError(DataSet: TDataSet; E: EDatabaseError;
UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
begin
{ ... Traitement des erreurs de mise à jour ... }
end;
Les sections suivantes décrivent des aspects précis de la gestion des erreurs à
l’aide d’un gestionnaire OnUpdateError ainsi que l’utilisation des paramètres de
l’événement.

Référencement de l’ensemble de données à mettre à jour


DataSet référence l’ensemble de données auquel s’applique la mise à jour. Pour
prendre en compte les nouvelles et anciennes valeurs d’enregistrements dans la
gestion des erreurs, vous devez fournir cette référence.

Indication du type de mise à jour ayant généré l’erreur


L’événement OnUpdateRecord reçoit le paramètre UpdateKind, qui est de type
TUpdateKind. Il décrit le type de mise à jour qui a provoqué l’erreur. Sauf si
votre gestionnaire d’erreur prend des mesures en fonction du type de mise à
jour effectué, il est probable que votre code n’utilisera pas ce paramètre.

25-28 Guide du développeur


Gestion des erreurs de mise à jour en mémoire cache

Le tableau ci-dessous donne la liste des valeurs possibles du paramètre UpdateKind :


Tableau 25.3 Valeurs du paramètre UpdateKind
Valeur Signification
ukModify L’édition d’un enregistrement existant a provoqué une erreur.
ukInsert L’insertion d’un nouvel enregistrement a provoqué une erreur.
ukDelete La suppression d’un enregistrement existant a provoqué une erreur.

La syntaxe suivante illustre l’exécution d’opérations en fonction de la valeur du


paramètre UpdateKind.
procedure TForm1.DataSetUpdateError(DataSet: TDataSet; E: EDatabaseError;
UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
begin
case UpdateKind of
ukModify:
begin
{ gestion d’erreur d’application de mise à jour de modification d’enregistrement }
end;
ukInsert:
begin
{ gestion d’erreur d’application de mise à jour d’insertion d’enregistrement }
end;
ukDelete:
begin
{ gestion d’erreur d’application de mise à jour de suppression d’enregistrement }
end;
end;
end;

Spécification de l’action à entreprendre


UpdateAction est un paramètre de type TUpdateAction. Lors du premier appel à
votre gestionnaire d’erreur de mise à jour, la valeur de ce paramètre est toujours
uaFail. Suivant la condition d’erreur de l’enregistrement qui a causé l’erreur et ce
que vous faites pour la corriger, vous donnerez généralement une valeur
différente à UpdateAction avant de quitter le gestionnaire. UpdateAction peut avoir
l’une des valeurs suivantes :
Tableau 25.4 Valeurs du paramètre UpdateAction
Valeur Signification
uaAbort Interrompt la mise à jour sans afficher de message d’erreur.
uaFail Interrompt la mise à jour et affiche un message d’erreur. C’est la valeur par
défaut d’UpdateAction lorsque vous entrez un gestionnaire d’erreur de mise à jour.
uaSkip Omet la mise à jour de la ligne mais laisse celle de l’enregistrement dans le cache.
uaRetry Répète la mise à jour. Corrige la condition d’erreur avant de donner cette valeur
à UpdateAction.
uaApplied N’est pas utilisée dans les routines de gestion des erreurs

Manipulation des mises à jour en mémoire cache 25-29


Gestion des erreurs de mise à jour en mémoire cache

Si votre gestionnaire d’erreur est en mesure de corriger la condition qui a suscité


son appel, donnez à UpdateAction la valeur de la mesure à prendre à la sortie.
Pour les erreurs que vous corrigez, vous devez donner la valeur uaRetry à
UpdateAction afin d’appliquer une nouvelle fois la mise à jour à l’enregistrement.
Lorsqu’elle a la valeur uaSkip, la mise à jour de la ligne qui a provoqué l’erreur
est omise et reste dans le cache quand toutes les autres sont terminées.
uaFail et uaAbort sont identiques et mettent un terme à toute l’opération de mise
à jour. uaFail provoque une exception et affiche un message d’erreur. uaAbort
provoque une exception silencieuse (elle n’affiche pas de message d’erreur).
Remarque S’il se produit une erreur lors de l’application des mises à jour en mémoire
cache, une exception est déclenchée et un message d’erreur s’affiche. S’il n’est
pas fait appel à ApplyUpdates depuis une construction try...except, l’affichage
d’un message d’erreur à l’intention de l’utlisateur depuis votre gestionnaire
d’événement OnUpdateError peut amener votre application à afficher le même
message deux fois. Donnez la valeur uaAbort à UpdateAction afin de désactiver
l’affichage du message d’erreur généré par le système.
La valeur uaApplied ne doit être utilisée que dans un gestionnaire d’événement
OnUpdateRecord. Vous ne devez pas l’appliquer à un gestionnaire d’erreur de
mise à jour. Pour plus de détails sur les événements de mise à jour des
enregistrements, reportez-vous à “Création d’un gestionnaire d’événement
OnUpdateRecord” à la page 25-26.

Manipulation du texte d’un message d’erreur


Le paramètre E est généralement de type EDBEngineError. De ce type
d’exception, vous pouvez extraire un message d’erreur que vous afficherez à
l’intention des utilisateurs dans votre gestionnaire d’erreur. Par exemple, vous
pouvez utiliser le code ci-dessous pour afficher le message d’erreur dans le
libellé d’une boîte de dialogue :
ErrorLabel.Caption := E.Message;
Ce paramètre est également utile pour déterminer la cause réelle de l’erreur de
mise à jour. Vous pouvez extraire des codes d’erreur spécifiques d’EDBEngineError
et prendre les mesures qui s’imposent en fonction de celui-ci. Par exemple, le code
ci-dessous vérifie si l’erreur de mise à jour est liée à une violation de clé et, si c’est
le cas, donne la valeur uaSkip au paramètre UpdateAction :
{ Ajoutez 'Bde' à votre clause uses pour cet exemple }
if (E is EDBEngineError) then
with EDBEngineError(E) do begin
if Errors[ErrorCount - 1].ErrorCode = DBIERR_KEYVIOL then
UpdateAction := uaSkip { violation de clé, ignore cet enregistrement }
else
UpdateAction := uaAbort; { ignore ce qui ne va pas, abandonne la mise à jour }
end;

25-30 Guide du développeur


Gestion des erreurs de mise à jour en mémoire cache

Accès aux propriétés OldValue, NewValue et CurValue d’un champ


Lorsque la mise à jour en mémoire cache est activée, la valeur originale des
champs de chaque enregistrement se trouve dans la propriété OldValue, une
propriété TField en lecture seulement. Les valeurs modifiées se trouvent, elles,
dans une propriété TField analogue, NewValue. Dans les ensembles de données
client, une propriété de type TField supplémentaire, CurValue, contient la valeur
du champ qui apparaît dans l’ensemble de données. CurValue a la même valeur
que OldValue sauf si un autre utilisateur a modifié l’enregistrement. Si un autre
utilisateur a modifié l’enregistrement, CurValue indique la valeur du champ
validée par cet utilisateur.
Ces valeurs sont le seul moyen d’inspecter et de modifier les valeurs de mise à
jour dans les gestionnaires d’événements OnUpdateError et OnUpdateRecord. Pour
en savoir plus sur OnUpdateRecord, voir “Création d’un gestionnaire d’événement
OnUpdateRecord” à la page 25-26.
Dans certaines situations, vous pouvez utiliser les propriétés OldValue, NewValue
et CurValue pour déterminer la cause d’une erreur et la corriger. Par exemple, le
code ci-dessous se charge des corrections dans un champ salaire dont la valeur
ne peut être augmentée que de 25 % à la fois (appliqué par une contrainte sur le
serveur) :
var
SalaryDif: Integer;
OldSalary: Integer;
begin
OldSalary := EmpTabSalary.OldValue;
SalaryDif := EmpTabSalary.NewValue - OldSalary;
if SalaryDif / OldSalary > 0.25 then begin
{ Increase was too large, drop it back to 25% }
EmpTabSalary.NewValue := OldSalary + OldSalary * 0.25;
UpdateAction := uaRetry;
end
else
UpdateAction := uaSkip;
end;
NewValue est ramenée à 25 % dans le cas où la mise à jour en mémoire cache
aurait augmenté le salaire dans une proportion supérieure. Une nouvelle
tentative de mise à jour a alors lieu. Pour améliorer l’efficacité de cette routine,
le paramètre OldValue est placé dans une variable locale.

Manipulation des mises à jour en mémoire cache 25-31


25-32 Guide du développeur
Chapitre

Utilisation de contrôles de données


Chapter 26
26
Ce chapitre décrit comment utiliser les contrôles visuels orientés données pour
afficher et éditer les données associées aux tables et aux requêtes de votre
application de base de données. Un contrôle orienté données dérive les données
affichées d’une base de données source hors de l’application. Il peut renvoyer les
modifications à la source de données.
Ce chapitre décrit les fonctionnalités élémentaires communes aux composants
contrôles de données, notamment comment et quand utiliser chaque composant.
La plupart des composants orientés données représentent des informations
stockées dans un ensemble de données. Ces informations peuvent être
regroupées dans des contrôles qui représentent un seul champ et des contrôles
qui représentent des ensembles d’enregistrements, tels que DBGrids et les grilles
de contrôle. De plus, le contrôle navigateur, TDBNavigator, est un outil visuel qui
permet aux utilisateurs de naviguer et de manipuler les enregistrements.
Les contrôles orientés données plus complexes d’aide à la décision sont présentés
chapitre 27, “Utilisation de composants d’aide à la décision”

Fonctionnalités communes des contrôles de données


Les tâches suivantes sont communes à la plupart des contrôles de données :
• Association d’un contrôle de données à un ensemble de données
• Edition et mise à jour des données
• Activation et désactivation de l’affichage des données
• Rafraîchissement de l’affichage des données
• Activation des événements souris, clavier et timer
Les contrôles de données doivent être placés sur les fiches de votre application
de base de données après avoir été sélectionnés sur la page ContrôleBD de la
palette des composants. Les contrôles orientés données vous permettent

Utilisation de contrôles de données 26-1


Fonctionnalités communes des contrôles de données

généralement d’afficher et d’éditer des champs associés à l’enregistrement en


cours d’un ensemble de données. Le tableau 26.1 dresse la liste des contrôles de
données apparaissant sur la page ContrôleBD de la palette des composants.

Tableau 26.1 Contrôles de données


Contrôle de données Description
TDBGrid Affiche les informations issues d’une source de données dans un
format tabulaire. Les colonnes de la grille correspondent à celles de la
table sous-jacente ou de l’ensemble de données de la requête. Chaque
ligne de la grille représente un enregistrement.
TDBNavigator Permet la navigation dans les enregistrements d’un ensemble de
données, leur mise à jour, leur validation, leur suppression, l’annulation
des opérations d’édition et le rafraîchissement de l’affichage.
TDBText Affiche les données d’un champ sous forme de libellé.
TDBEdit Affiche les données d’un champ dans une boîte de saisie.
TDBMemo Affiche des données d’un champ mémo ou d’un champ BLOB dans
une boîte de saisie multiligne.
TDBImage Affiche des images graphiques d’un champ de données dans une boîte
graphique.
TDBListBox Affiche une liste d’éléments à partir desquels vous pouvez mettre à
jour un champ de l’enregistrement en cours.
TDBComboBox Affiche une liste déroulante d’éléments à partir desquels vous pouvez
mettre à jour un champ et permet la saisie directe de texte comme
dans une boîte de saisie standard.
TDBCheckBox Affiche une case à cocher indiquant la valeur d’un champ booléen.
TDBRadioGroup Affiche un ensemble d’options mutuellement exclusives pour un champ.
TDBLookupListBox Affiche une liste d’éléments référencés dans un autre ensemble de
données en fonction de la valeur d’un champ.
TDBLookupComboBox Affiche une liste d’éléments référencés dans un autre ensemble de
données en fonction de la valeur d’un champ et permet la saisie directe
de texte comme dans une boîte de saisie orientée données standard.
TDBCtrlGrid Affiche un ensemble de contrôles orientés données réitéré et
configurable dans une grille.
TDBRichEdit Affiche les données mises en forme d’un champ dans une boîte de
saisie.

Les contrôles sont orientés données au moment de la conception. Lorsque vous


affectez à la propriété DataSource d’un contrôle une source de données active lors
de la construction d’une application, vous pouvez voir immédiatement les
données réelles dans les contrôles. En phase de conception, vous pouvez utiliser
l’éditeur de champs pour parcourir un ensemble de données afin de vérifier que
votre application affiche les données correctement sans qu’il soit nécessaire de la
compiler ou de l’exécuter. Pour plus d’informations sur l’éditeur de champs, voir
la section “Création de champs persistants” à la page 19-6 du chapitre 19,
“Manipulation des composants champ”.
Lors de l’exécution, les contrôles orientés données affichent également des
données et permettent leur édition si le contrôle, l’application et la base de
données auxquels votre application est connectée le permettent.

26-2 Guide du développeur


Fonctionnalités communes des contrôles de données

Association d’un contrôle de données à un ensemble de données


Les contrôles de données se connectent aux ensembles de données en utilisant
une source de données. Un composant source de données agit comme une voie
de communication entre le contrôle et un ensemble de données contenant des
données. Pour plus de détails, voir “Utilisation des sources de données” à la
page 26-6.
Pour associer un contrôle de données à un ensemble de données,
1 Placez un ensemble de données et une source de données dans un module de
données ou sur une fiche et définissez leurs propriétés.
2 Depuis l’onglet AccèsBD de la palette des composants, placez un contrôle de
données sur une fiche.
3 Donnez à la propriété DataSource du contrôle le nom du composant source de
données d’où doivent provenir les données.
4 Donnez à la propriété DataField du contrôle le nom du champ à afficher, ou
bien sélectionnez un champ dans la liste déroulante. Cette étape ne s’applique
pas aux contrôles TDBGrid, TDBCtrlGrid et TDBNavigator, car ils accèdent à
tous les champs disponibles dans un ensemble de données.
5 Pour afficher des données dans le contrôle, mettez la propriété Active de
l’ensemble de données à True.

Edition et mise à jour des données


A l’exception du navigateur, tous les contrôles affichent les données d’un champ
de base de données. De plus, vous pouvez les utiliser pour éditer et mettre à
jour les données, si l’ensemble de données sous-jacent le permet.

Activation de l’édition des contrôles lors d’une saisie utilisateur


Un ensemble de données doit être en mode dsEdit pour autoriser l’édition de son
contenu. La propriété AutoEdit de la source de données à laquelle un contrôle est
attaché détermine si l’ensemble de données sous-jacent entre en mode dsEdit lors
de la modification des données d’un contrôle par suite d’événements souris ou
clavier. Quand AutoEdit vaut True (valeur par défaut), le mode dsEdit est défini
dès que débute l’édition. Si AutoEdit vaut False, vous devez fournir un bouton
d’édition (ou une méthode quelconque) à un contrôle TDBNavigator pour
permettre aux utilisateurs de définir le mode dsEdit lors de l’exécution. Pour en
savoir plus sur le composant TDBNavigator, voir “Navigation et manipulation
d’enregistrements” à la page 26-32.

Edition des données affichées dans un contrôle


La propriété ReadOnly d’un contrôle de données détermine si un utilisateur peut
éditer les données affichées. Si elle vaut False (valeur par défaut), l’édition est
possible. Pour interdire aux utilisateurs d’éditer ces données, mettez ReadOnly à
True.

Utilisation de contrôles de données 26-3


Fonctionnalités communes des contrôles de données

Les propriétés de la source de données et de l’ensemble de données sous-jacents


d’un contrôle déterminent également si l’utilisateur peut éditer les données du
contrôle et valider les modifications dans l’ensemble de données.
La propriété Enabled d’une source de données détermine si les contrôles attachés
à une source de données peuvent afficher la valeur des champs de l’ensemble de
données. De ce fait, elle détermine également si un utilisateur peut éditer et
écrire les valeurs. Si Enabled vaut True (valeur par défaut), les contrôles peuvent
afficher la valeur des champs.
La propriété ReadOnly de l’ensemble de données détermine si les modifications
de l’utilisateur peuvent être validées dans l’ensemble de données. Si sa valeur est
False (valeur par défaut), les modifications sont validées. Si elle est True,
l’ensemble de données est en lecture seulement.
Remarque Une autre propriété d’exécution en lecture seulement, CanModify, détermine si un
ensemble de données peut être modifié. CanModify est définie à True si une base
de données autorise l’accès en écriture. Si CanModify est à False, l’ensemble de
données est en lecture seulement. Les composants requête qui effectuent des
insertions et des mises à jour peuvent, par définition, écrire dans une base de
données sous-jacente dans la mesure où votre application et l’utilisateur
disposent de privilèges d’accès à la base de données.
Le tableau suivant donne la liste des facteurs déterminant si un utilisateur peut
éditer les données d’un contrôle et écrire les modifications dans la base de
données.
Tableau 26.2 Propriétés affectant l’édition dans les contrôles de données
Composant Propriété
Contrôle ReadOnly false false false true
données
Source de Enabled true true false —
données
Ensemble de ReadOnly false false — —
données
Ensemble de CanModify true false — —
données
(base de accès en écriture lecture/écriture lecture seule — —
données)
Ecriture dans la base de données? Oui Non Non Non

Dans tous les contrôles orientés données, à l’exception de TDBGrid, les


modifications d’un champ sont copiées dans le composant champ sous-jacent
d’un ensemble de données au moment où vous quittez le contrôle. Si vous
appuyez sur la touche Echap avant d’appuyer sur Tab dans un champ, les
modifications sont abandonnées et le champ reprend sa valeur initiale.
Avec le composant TDBGrid, les modifications ne sont copiées qu’au moment où
vous passez à un enregistrement différent ; vous pouvez appuyer sur Echap dans
n’importe quel champ d’un enregistrement avant de passer à un autre
enregistrement pour annuler une modification.

26-4 Guide du développeur


Fonctionnalités communes des contrôles de données

Lors de l’écriture d’un enregistrement, Delphi recherche les changements d’état


dans tous les composants orientés données associés à l’ensemble de données. Si
un problème survient lors de la mise à jour de champs contenant des données
modifiées, Delphi provoque une exception et aucune modification n’est faite dans
l’enregistrement.

Activation et désactivation de l’affichage des données


Lorsque votre application parcourt un ensemble de données ou effectue une
recherche, il est préférable d’interdire temporairement le rafraîchissement des
valeurs affichées dans les contrôles orientés données à chaque changement
d’enregistrement. Cela a pour effet d’accélérer l’itération ou la recherche et
permet d’éviter que l’image ne “saute” à l’écran.
DisableControls est une méthode qui désactive l’affichage dans tous les contrôles
orientés données liés à un ensemble de données. Dès que l’itération ou la
recherche est terminée, votre application doit immédiatement faire appel à la
méthode EnableControls pour réactiver l’affichage des contrôles.
En règle générale, vous devez désactiver les contrôles avant de commencer à
parcourir les enregistrements. Ce processus doit prendre place dans une
instruction try...finally pour que vous puissiez réactiver les contrôles même si
une exception est provoquée. La clause finally doit faire appel à EnableControls
en plus de l’appel de EnableControls hors du bloc try...finally. Le code ci-dessous
montre comment DisableControls et EnableControls peuvent être utilisées dans ce
but :
CustTable.DisableControls;
try
CustTable.First; { Accède au premier enregistrement et met Eof à False }
while not CustTable.EOF do { Cycle jusqu’à ce que EOF soit à True }
begin
{ Traitement de chaque enregistrement ici }
ƒ
CustTable.Next; { EOF vaut False en cas de réussite; EOF vaut True lorsque Next échoue
sur le dernier enregistrement }
end;
finally
CustTable.EnableControls;
end;

Rafraîchissement de l’affichage des données


La méthode Refreshd’un ensemble de données réinitialise les tampons locaux et
extrait à nouveau les données d’un ensemble ouvert. Vous pouvez utiliser cette
méthode pour mettre à jour l’affichage dans les contrôles orientés données si
vous pensez que les données sous-jacentes ont changé du fait que d’autres
applications y accèdent en même temps.

Utilisation de contrôles de données 26-5


Utilisation des sources de données

Important Le rafraîchissement donne parfois des résultats inattendus (par exemple,


lorsqu’un utilisateur est en train de visualiser un enregistrement supprimé par
une autre application, puis que cet enregistrement disparaît dès que l’application
fait appel à Refresh). Les données peuvent également changer à l’affichage si un
autre utilisateur modifie un enregistrement après que vous ayez extrait les
données et avant d’avoir fait appel à Refresh.

Activation des événements souris, clavier et timer


La propriété Enabled d’un contrôle de données détermine s’il doit réagir aux
événements souris, clavier ou timer et transmettre des informations à sa source
de données. La valeur par défaut de cette propriété est True.
Pour empêcher les événements souris, clavier ou timer d’accéder à un contrôle
de données, vous devez définir sa propriété Enabled à False. Dans ce cas, la
source de données ne reçoit pas d’informations du contrôle de données. Celui-ci
affichera toujours les données, mais le texte n’apparaîtra pas en surbrillance.

Utilisation des sources de données


Un composant TDataSource est un composant base de données non visuel qui
agit comme un canal de communication entre un ensemble de données et les
composants orientés données d’une fiche. De plus, il active l’affichage, la
navigation et l’édition de données sous-jacentes à l’ensemble de données. Chaque
contrôle orienté données doit avoir un composant source de données qui lui
correspond afin de pouvoir afficher et manipuler des données. De même, chaque
ensemble de données doit être associé à un composant source de données pour
que ses données puissent être affichées ou manipulées dans les contrôles orientés
données d’une fiche.
Remarque Quand vous utilisez les composants d’accès aux données de la page Interbase de
la palette des composants, vous devez utiliser un TIBDataSource à la place. Le
composant TIBDataSource fonctionne exactement comme un TDataSource.
Remarque Vous pouvez aussi utiliser les composants source de données pour lier les
ensembles de données dans des relations maître-détail.
Un composant source de données se place dans un module de données ou dans
une fiche comme tout autre composant base de données non visuel. Vous devez
placer au moins un composant source de données pour chaque composant
ensemble de données d’un module de données ou d’une fiche.

Utilisation des propriétés de TDataSource


Les composants TDataSource n’ont que quelques propriétés publiées. Les sections
suivantes abordent ces propriétés clés et expliquent comment les initialiser lors
de la conception ou de l’exécution.

26-6 Guide du développeur


Utilisation des sources de données

Propriété DataSet
La propriété DataSet spécifie l’ensemble de données qui fournit les données au
composant source de données. Généralement, les ensembles de données sont
sélectionnés lors de la conception à partir de la liste déroulante de l’inspecteur
d’objets. A l’exécution, vous pouvez changer l’ensemble de données d’un
composant source de données en fonction des besoins. Le code suivant fait
permuter l’ensemble de données entre Customers et Orders pour le composant
source de données CustSource :
with CustSource do
begin
if DataSet = 'Customers' then
DataSet := 'Orders'
else
DataSet := 'Customers';
end;
Vous pouvez aussi définir la propriété DataSet avec un ensemble de données se
trouvant sur une autre fiche afin de synchroniser les contrôles de données des
deux fiches. Par exemple :
procedure TForm2.FormCreate (Sender : TObject);
begin
DataSource1.Dataset := Form1.Table1;
end;

Propriété Name
La propriété Name sert à donner un nom à un composant source de données de
façon à le distinguer des autres sources de données dans votre application. Dans
un module de données, le nom attribué au composant source de données
s’affiche sous son icône.
Généralement, le nom affecté au composant source de données doit donner une
indication sur l’ensemble de données auquel il est associé. Supposons un
ensemble de données, appelé Clients, auquel vous rattachez un composant source
de données en affectant “Clients” à la propriété DataSet du composant source de
données. Pour mettre en évidence la connexion entre l’ensemble de données et la
source dans le module de données, il est souhaitable de définir la propriété Name
de la source avec une valeur comme “SourceClients”.

Propriété Enabled
La propriété Enabled détermine si le composant source de données est connecté à
l’ensemble de données qui lui est associé. La source de données est effectivement
connectée quand Enabled est à True.
Vous pouvez temporairement déconnecter une source de données unique de son
ensemble de données en basculant Enabled à False. Si la valeur de la propriété
Enabled vaut False, tous les contrôles orientés données rattachés au composant
source sont vidés et deviennent inactifs jusqu’à ce que Enabled bascule une
nouvelle fois à True. Toutefois, il est recommandé de contrôler l’accès à un
ensemble de données via ses méthodes DisableControls et EnableControls car elles
agissent sur toutes les sources de données rattachées.

Utilisation de contrôles de données 26-7


Utilisation des sources de données

Propriété AutoEdit
La propriété AutoEdit de TDataSource indique si les ensembles de données
connectés à la source de données passent automatiquement en mode édition
quand l’utilisateur débute une frappe de touches dans un contrôle orienté
données lié à l’ensemble de données. Si AutoEdit est à True (la valeur par
défaut), l’ensemble de données passe automatiquement en mode édition quand
l’utilisateur commence une frappe de touches dans un contrôle orienté données
lié. Sinon, l’ensemble de données ne passe en mode édition que si l’application
appelle explicitement sa méthode Edit. Pour plus d’informations concernant les
états d’un ensemble de données, voir “Détermination et définition des états d’un
ensemble de données” au chapitre 18, “Présentation des ensembles de données”.

Utilisation des événements de TDataSource


TDataSource dispose de trois gestionnaires d’événements qui lui sont associés :
• OnDataChange
• OnUpdateData
• OnStateChange

Evénement OnDataChange
OnDataChange est appelé quand le curseur se déplace sur un nouvel
enregistrement. Quand l’application appelle Next, Previous, Insert ou toute autre
méthode qui provoque le changement de position du curseur, un événement
OnDataChange est déclenché.
Cet événement est utile si une application synchronise manuellement des
composants.

Evénement OnUpdateData
OnUpdateData est appelé quand les données dans l’enregistrement en cours sont
sur le point d’être mises à jour. Par exemple, un événement OnUpdateData se
produit après un appel à Post , mais aussi avant que les données ne soient
effectivement émises dans la base de données.
Cet événement est utile si une application utilise un contrôle standard (non
orienté données) et qu’elle doit assurer sa synchronisation avec un ensemble de
données.

Evénement OnStateChange
OnStateChange est appelé quand l’état d’un ensemble de données associé à la
source de données change. La propriété State d’un ensemble de données sert à
mémoriser son état actuel. OnStateChange peut servir pour accomplir des actions
au fur et à mesure qu’évolue l’état d’un composant TDataSource.
Par exemple, l’état d’un ensemble de données change fréquemment dans le cadre
habituel d’une session de base de données. Pour suivre ces changements, vous
pourriez écrire un gestionnaire d’événement OnStateChange qui affiche l’état en

26-8 Guide du développeur


Contrôles représentant un champ unique

cours de l’ensemble de données dans le libellé d’une fiche. Le code suivant


illustre l’une des manières possibles de programmer une telle routine. A
l’exécution, ce code affiche le paramétrage en cours de la propriété State de
l’ensemble de données en mettant à jour l’affichage à chaque fois qu’il change :
procedure TForm1.DataSource1.StateChange(Sender:TObject);
var
S:String;
begin
case CustTable.State of
dsInactive: S := 'Inactive';
dsBrowse: S := 'Browse';
dsEdit: S := 'Edit'; dsInsert: S := 'Insert';
dsSetKey: S := 'SetKey';
end;
CustTableStateLabel.Caption := S;
end;
De façon similaire, OnStateChange peut être utilisé pour activer ou désactiver des
boutons ou des éléments de menu selon l’état en cours :
procedure Form1.DataSource1.StateChange(Sender: TObject);
begin
CustTableEditBtn.Enabled := (CustTable.State = dsBrowse);
CustTableCancelBtn.Enabled := CustTable.State in [dsInsert, dsEdit, dsSetKey];
...
end;

Contrôles représentant un champ unique


De nombreux contrôles de la page des contrôles de données de la palette des
composants représentent un champ unique d’une table de base de données. La
plupart de ces contrôles sont semblables en apparence et dans leur fonction aux
contrôles Windows standard que vous placez sur les fiches. Par exemple, le
contrôle TDBEdit est une version orientée données du contrôle TEdit standard
qui permet aux utilisateurs de visualiser et d’éditer une chaîne texte.
Le type de données contenu dans le champ (texte, texte formaté, graphique,
informations booléennes, etc.) détermine le contrôle utilisé.

Affichage de données en tant que libellés


TDBText est un contrôle en lecture seulement semblable au composant TLabel de
l’onglet Standard de la palette des composants et au composant TStaticText de la
page Supplément. Le contrôle TDBText est utile quand vous voulez placer des
données en affichage seulement sur une fiche permettant à l’utilisateur d’entrer
des données dans d’autres contrôles. Supposons, par exemple, qu’une fiche soit
créée à partir des champs d’une table clients et qu’une fois que l’utilisateur entre
les informations sur la rue, la ville et le département dans la fiche, vous utilisiez
une référence dynamique pour déterminer automatiquement le contenu du

Utilisation de contrôles de données 26-9


Contrôles représentant un champ unique

champ code postal depuis une table distincte. Un composant TDBText relié à la
table des codes postaux permettra d’afficher le champ code postal correspondant
à l’adresse entrée par l’utilisateur.
TDBText extrait le texte affiché du champ spécifié dans l’enregistrement en cours
d’un ensemble de données. De ce fait, le texte affiché est dynamique, c’est-à-dire
qu’il change au fur et à mesure que l’utilisateur navigue dans la table. Pour cette
raison, il ne vous est pas possible de spécifier le texte d’affichage de TDBText au
moment de la conception comme c’est le cas pour les composants TLabel et
TStaticText
Remarque Lorsque vous placez un composant TDBText sur une fiche, vérifiez que sa
propriété AutoSize est à True (la valeur par défaut) ; vous garantissez ainsi que le
contrôle se redimensionne automatiquement pour afficher des données de
largeur variable. Si AutoSize est à False et le contrôle trop étroit, les données
affichées seront tronquées.

Affichage et édition de champs dans une boîte de saisie


TDBEdit est la version orientée données du composant boîte de saisie. Il affiche
la valeur actuelle d’un champ de données auquel il est lié et lui permet d’être
édité comme dans n’importe quelle boîte de saisie.
Si, par exemple, CustomersSource est un composant TDataSource actif et lié à un
composant TTable appelé CustomersTable, vous pouvez placer un composant
TDBEdit sur une fiche et définir ses propriétés comme suit :
• DataSource : CustomersSource
• DataField : CustNo
Le composant boîte de saisie orientée données affiche immédiatement la valeur
de la colonne CustNo de la ligne en cours dans l’ensemble de données
CustomersTable, tant au moment de la conception que de l’exécution.

Affichage et édition de texte dans un contrôle mémo


TDBMemo est un composant orienté données, semblable au composant TMemo
standard, permettant d’afficher des données au format BLOB (binary large
object). TDBMemo sert à afficher et saisir du texte multiligne. Vous pouvez
utiliser les contrôles TDBMemo pour afficher les champs mémo de tables dBASE
et Paradox ainsi que des données texte contenues dans des champs BLOB.
Par défaut, TDBMemo permet à un utilisateur d’éditer du texte mémo. Pour
empêcher l’édition, mettez la propriété ReadOnly du contrôle mémo à True. Pour
permettre aux utilisateurs d’entrer des tabulations dans le texte, mettez la
propriété WantTabs à True. Pour limiter le nombre de caractères pouvant être
saisis par les utilisateurs dans un mémo de base de données, utilisez la propriété
MaxLength. Par défaut, MaxLength vaut 0, ce qui signifie qu’il n’y a aucune
limite, en dehors de celles du système d’exploitation, au nombre de caractères
pouvant être contenus dans le contrôle.

26-10 Guide du développeur


Contrôles représentant un champ unique

Plusieurs propriétés affectent l’aspect du mémo de base de données et la façon


d’entrer le texte. Vous pouvez fournir des barres de défilement à l’aide de la
propriété ScrollBars. Pour interdire le retour automatique en fin de ligne,
définissez la propriété WordWrap à False. La propriété Alignment détermine
l’alignement du texte dans le contrôle. Les options possibles sont taLeftJustify (par
défaut), taCenter et taRightJustify. Pour changer la fonte du texte, utilisez la
propriété Font.
Au moment de l’exécution, l’utilisateur peut couper, copier et coller du texte vers
un contrôle mémo ou depuis celui-ci. Vous pouvez également effectuer cette
tâche par programmation avec les méthodes CutToClipboard, CopyToClipboard et
PasteFromClipboard.
Comme TDBMemo peut afficher de grandes quantités de données, ce champ peut
être relativement long à remplir lors de l’exécution. Pour accélérer le défilement
des enregistrements, TDBMemo a une propriété AutoDisplay qui contrôle si les
données auxquelles il accède doivent être affichées automatiquement. Si vous
définissez AutoDisplay à False, TDBMemo affiche le nom du champ plutôt que les
données elles-mêmes. Pour les voir, double-cliquez à l’intérieur du contrôle.

Affichage et édition dans un contrôle mémo de texte formaté


TDBRichEdit est un composant orienté données, semblable au composant
TRichEdit standard, qui peut afficher le texte formaté d’un BLOB (binary large
object). TDBMemo affiche du texte formaté, multiligne, et permet à l’utilisateur
d’entrer du texte multiligne formaté. Vous pouvez utiliser les contrôles
TDBRichEdit pour afficher des champs mémo de tables dBASE et Paradox et des
données texte contenues dans des champs BLOB.
Remarque TDBRichEdit est doté de propriétés et de méthodes permettant d’entrer et de
manipuler du texte formaté. Toutefois, il n’offre pas une interface utilisateur
mettant ces options de formatage à la disposition de l’utilisateur. C’est votre
application qui doit implémenter l’interface utilisateur permettant d’accéder aux
fonctions de texte formaté.
Par défaut, TDBRichEdit permet à l’utilisateur d’éditer du texte mémo formaté.
Pour empêcher l’édition du texte, mettez la propriété ReadOnly du contrôle à
True. Pour afficher les tabulations et permettre aux utilisateurs d’entrer des
tabulations dans le mémo, mettez la propriété WantTabs à True. Pour limiter le
nombre de caractères pouvant être saisis par les utilisateurs dans un mémo de
base de données, utilisez la propriété MaxLength. Par défaut, MaxLength vaut 0,
ce qui signifie qu’il n’y a aucune limite, en dehors de celles du système
d’exploitation, au nombre de caractères pouvant être saisi.
Comme TDBRichEdit peut contenir de grands volumes de données, il faut parfois
beaucoup de temps pour afficher les données à l’exécution. Pour réduire le
temps de défilement des enregistrements, utilisez la propriété AutoDisplay. Elle
détermine si les données sont automatiquement affichées. Si AutoDisplay est à
False , TDBRichEdit affiche le nom du champ à la place des données. Pour
visualiser les données, il suffit de double-cliquer à l’intérieur du contrôle.

Utilisation de contrôles de données 26-11


Contrôles représentant un champ unique

Affichage et édition de champs graphiques dans un contrôle image


TDBImage est un composant orienté données qui permet l’affichage d’images
bitmap contenues dans des champs BLOB. Il les capture à partir d’un ensemble
de données et les stocke en interne au format Windows.DIB.
Par défaut, TDBImage permet à l’utilisateur d’éditer une image par couper-coller
via le Presse-papiers en utilisant les méthodes CutToClipboard, CopyToClipboard et
PasteFromClipboard. Vous pouvez également fournir vos propres méthodes
d’édition si vous le souhaitez.
Par défaut, seule la partie du graphique pouvant tenir dans le contrôle image
apparaît. Vous pouvez mettre la propriété Stretch à True pour redimensionner le
graphique afin qu’il remplisse le contrôle image et que sa taille soit ajustée
conformément aux redimensionnements du contrôle.
Comme le composant TDBImage peut afficher de grandes quantités de données,
l’affichage peut prendre un certain temps à l’exécution. Pour accélérer le
défilement des enregistrements, TDBImage a une propriété AutoDisplay qui
contrôle si les données auxquelles il accède doivent s’afficher automatiquement.
Si vous mettez AutoDisplay à False, TDBImage affiche le nom du champ au lieu
des données. Pour voir les données, double-cliquez à l’intérieur du contrôle.

Affichage de données dans des boîtes liste et des boîtes à options


Quatre contrôles de données présentent une version orientée données des
contrôles boîte liste et boîte à options standard. Ces contrôles fournissent aux
utilisateurs un groupe de valeurs par défaut parmi lesquelles il est possible
d’effectuer un choix à l’exécution.
Remarque Les boîtes liste et les boîtes à options orientées données ne peuvent être liées
qu’aux sources de données de composants table. Elles ne fonctionnent pas avec
les composants requête.
Le tableau ci-dessous donne la description de ces contrôles :
Tableau 26.3 Contrôles boîte à options et boîte liste orientés données
Contrôle de données Description
TDBListBox Affiche une liste d’éléments à partir desquels l’utilisateur peut
mettre à jour un champ de l’enregistrement en cours. La liste des
éléments affichés est définie par une propriété du contrôle.
TDBComboBox Combine une boîte de saisie et une boîte liste. L’utilisateur peut
mettre à jour un champ de l’enregistrement en cours en choisissant
une valeur dans la liste déroulante ou en la saisissant. La liste des
éléments affichés est définie par une propriété du contrôle.
TDBLookupListBox Affiche une liste d’éléments à partir desquels l’utilisateur peut
mettre à jour une colonne de l’enregistrement en cours. La liste des
éléments affichés est référencée dans un autre ensemble de données.
TDBLookupComboBox Combine une boîte de saisie et une boîte liste. L’utilisateur peut
mettre à jour un champ de l’enregistrement en cours en choisissant
une valeur dans la liste déroulante ou en la saisissant. La liste des
éléments affichés est référencée dans un autre ensemble de données.

26-12 Guide du développeur


Contrôles représentant un champ unique

Affichage et édition de données dans une boîte liste


Le composant TDBListBox affiche une liste d’éléments qu’il est possible de faire
défiler. L’utilisateur peut aussi choisir l’un de ces éléments pour entrer dans un
champ de données. Une boîte liste orientée données affiche la valeur en cours
pour un champ de l’enregistrement en cours et met l’entrée correspondante en
surbrillance dans la liste. Si la valeur de champ de la ligne en cours ne figure
pas dans la liste, aucune valeur n’est mise en surbrillance dans la boîte liste.
Quand un utilisateur sélectionne un élément de la liste, la valeur de champ
correspondante est modifiée dans l’ensemble de données sous-jacent.
Lors de la phase de conception, vous pouvez utiliser l’éditeur de liste de chaîne
pour créer une liste d’éléments à afficher dans la propriété Items. La propriété
Height détermine la quantité d’éléments visibles dans la boîte liste. La propriété
IntegralHeight contrôle l’affichage de la boîte liste. Si IntegralHeight est à False
(valeur par défaut), la position inférieure de la liste est déterminée par la propriété
ItemHeight et le dernier élément peut ne pas s’afficher entièrement. Si IntegralHeight
est à True, le dernier élément visible de la boîte liste s’affiche entièrement.

Affichage et édition de données dans une boîte à options


Le contrôle TDBComboBox combine les fonctionnalités d’un contrôle de saisie
orienté données et d’une liste déroulante. Lors de l’exécution, les utilisateurs
peuvent mettre à jour un champ de l’enregistrement en cours dans un ensemble
de données en tapant une valeur ou en sélectionnant une valeur dans la liste
déroulante. A l’exécution, les utilisateurs ont la possibilité d’afficher une liste
déroulante dans laquelle ils peuvent sélectionner un ensemble de valeurs
prédéfinies ou de saisir une valeur différente.
La propriété Items du composant spécifie les éléments contenus dans la liste
déroulante. Lors de la conception, utilisez l’éditeur de liste de chaîne pour
remplir la liste de la propriété Items. A l’exécution, utilisez les méthodes de la
propriété Items pour manipuler la liste de chaînes.
Quand un contrôle est lié à un champ par sa propriété DataField, il affiche la
valeur du champ de la ligne en cours, qu’elle apparaisse ou non dans la liste
Items. La propriété Style détermine l’interaction de l’utilisateur avec le contrôle.
Par défaut, Style est à csDropDown, ce qui signifie que l’utilisateur peut entrer
des valeurs au clavier ou bien choisir un élément dans la liste déroulante. Les
propriétés suivantes déterminent l’affichage de la liste Items lors de l’exécution :
• Style détermine le style d’affichage du composant :
• csDropDown (valeur par défaut) : affiche une liste déroulante avec une boîte
de saisie où l’utilisateur peut entrer du texte. Tous les éléments sont des
chaînes de même hauteur.
• csSimple : combine un contrôle de saisie à une liste d’éléments de taille fixe
systématiquement affichée. Quand Style a la valeur csSimple, assurez-vous
d’augmenter la propriété Height afin de pouvoir afficher la liste.
• csDropDownList : affiche une boîte liste et une boîte de saisie, mais
l’utilisateur ne peut ni saisir ni modifier les valeurs qui ne figurent pas
dans la liste déroulante au moment de l’exécution.

Utilisation de contrôles de données 26-13


Contrôles représentant un champ unique

• csOwnerDrawFixed et csOwnerDrawVariable : permettent à la liste des éléments


d’afficher des valeurs autres que des chaînes (par exemple, des images
bitmap) ou d’utiliser des fontes différentes pour les éléments de la liste.
• DropDownCount : nombre maximum d’éléments affichés dans la liste. Si le
nombre Items est supérieur à DropDownCount, l’utilisateur peut faire défiler la
liste. S’il est inférieur à DropDownCount, la liste sera juste assez grande pour
afficher tous les éléments.
• ItemHeight : hauteur de chaque élément quand Style vaut csOwnerDrawFixed.
• Sorted : si à True, la liste Items s’affiche par ordre alphabétique.

Affichage dans une boîte liste de référence et une boîte à options


de référence
TDBLookupListBox et TDBLookupComboBox sont des contrôles orientés données qui
dérivent une liste d’éléments d’affichage depuis l’une des deux sources suivantes :
• un champ de référence défini pour un ensemble de données ;
• une clé, un champ de données et une source de données secondaires.
Dans un cas comme dans l’autre, l’utilisateur se voit proposer une liste limitée
de choix à partir desquels il peut définir une valeur de champ correcte.
Lorsqu’un utilisateur sélectionne un élément dans la liste, la valeur de champ
correspondante est remplacée dans l’ensemble de données sous-jacent.
Prenons l’exemple d’un bon de commande dont les champs sont liés à
OrdersTable. OrdersTable contient un champ CustNo correspondant à un numéro
d’ID client, mais OrdersTable ne contient aucune autre information sur le client.
CustomersTable, en revanche, contient un champ CustNo correspondant à un
numéro d’ID client ainsi que des informations supplémentaires, telles que
l’entreprise et l’adresse du client. Lors de la facturation, il serait pratique que le
bon de commande permette à un employé de sélectionner un client d’après le
nom de l’entreprise au lieu de l’ID client. Un composant TDBLookupListBox
affichant le nom de toutes les entreprises dans CustomersTable permettra à un
utilisateur de sélectionner le nom de l’entreprise dans la liste et de placer CustNo
sur le bon de commande.

Spécification d’une liste basée sur un champ de référence


Pour spécifier les éléments d’une boîte liste à l’aide d’un champ de référence,
l’ensemble de données auquel vous liez le contrôle doit déjà définir un champ de
référence dans l’ensemble de données. Pour en savoir plus sur la définition d’un
champ de référence pour un ensemble de données, reportez-vous à “Définition
d’un champ de référence” à la page 19-11 du chapitre 19, “Manipulation des
composants champ”.
Pour spécifier un champ de référence pour les éléments d’une boîte liste,
1 Affectez à la propriété DataSource de la boîte liste la source de données de
l’ensemble de données contenant le champ de référence à utiliser.

26-14 Guide du développeur


Contrôles représentant un champ unique

2 Choisissez le champ de référence à utiliser dans la liste déroulante de la


propriété DataField.
Lorsque vous activez une table associée à une boîte liste de référence, il identifie
son champ de données en tant que champ de référence et affiche les valeurs
appropriées.

Spécification d’une liste d’après une source de données secondaire


Si vous n’avez pas défini de champ de référence pour un ensemble de données,
vous pouvez établir une relation de même type à l’aide d’une source de données
secondaire, d’une valeur de champ à rechercher dans la source de données
secondaire et d’une valeur de champ à renvoyer comme élément de liste.
Pour spécifier une source de données secondaire pour les éléments d’une boîte liste,
1 Affectez à la propriété DataSource de la boîte liste la source de données du
contrôle.
2 Choisissez un champ pour insérer les valeurs de référence depuis la liste
déroulante de la propriété DataField. Le champ choisi ne doit pas être un
champ de référence.
3 Affectez à la propriété ListSource de la boîte liste la source de l’ensemble de
données contenant le champ dont vous voulez référencer les valeurs.
4 Pour la propriété KeyField, choisissez dans la liste déroulante un champ à
utiliser comme clé de référence. La liste déroulante affiche les champs de
l’ensemble de données associé à la source de données spécifiée à l’étape 3. Le
champ choisi n’a pas besoin de faire partie d’un index, mais si c’est le cas, les
performances en seront améliorées.
5 Dans la liste déroulante de la propriété ListField, choisissez un champ dont les
valeurs sont à renvoyer. Cette liste déroulante affiche les champs de
l’ensemble de données associé à la source que vous avez spécifiée à l’étape 3.
Lorsque vous activez une table associée à un contrôle boîte liste de référence, il
reconnaît que les éléments de sa liste sont dérivés d’une source secondaire et
affiche des valeurs issues de celle-ci.

Propriétés des boîtes liste et des boîtes à options de référence


Le tableau suivant dresse la liste des principales propriétés des boîtes liste et
boîtes à options de référence :

Tableau 26.4 Propriétés de TDBLookupListBox et TDBLookupComboBox


Propriété Finalité
DataField Spécifie le champ de l’ensemble de données maître qui fournit la valeur
clé à rechercher dans l’ensemble de données de référence. Ce champ est
modifié lorsqu’un utilisateur sélectionne un élément dans la boîte liste ou
dans la boîte à options. Si DataField définit un champ de référence, les
propriétés KeyField, ListField et ListSource ne sont pas utilisées.
DataSource Spécifie une source de données pour le contrôle. Si la sélection dans le
contrôle change, cet ensemble de données passe en mode dsEdit.

Utilisation de contrôles de données 26-15


Contrôles représentant un champ unique

Tableau 26.4 Propriétés de TDBLookupListBox et TDBLookupComboBox (suite)


Propriété Finalité
KeyField Spécifie le champ de l’ensemble de données de référence correspondant à
DataField. Le contrôle recherche la valeur de DataField dans la propriété
KeyField de l’ensemble de données de référence. L’ensemble de données de
référence doit avoir un index sur ce champ pour faciliter les recherches.
ListField Spécifie le champ de l’ensemble de données de référence à afficher dans le
contrôle.
ListSource Spécifie une source de données pour l’ensemble de données secondaire.
L’ordre de tri des éléments affichés dans la boîte liste ou dans la boîte à
options est déterminé par l’index spécifié dans la propriété IndexName de
l’ensemble de données de référence. Il n’est pas nécessaire que cet index
soit le même que celui de la propriété KeyField.
RowCount TDBLookupListBox uniquement. La hauteur de celle-ci est ajustée pour
correspondre exactement à ce nombre de lignes.
DropDownRows TDBLookupComboBox uniquement. Spécifie le nombre de lignes de texte
à afficher dans la liste déroulante.

Recherche incrémentale dans les valeurs d’une liste d’éléments


Au moment de l’exécution, les utilisateurs peuvent lancer une recherche
incrémentale pour trouver des éléments dans une boîte liste. Lorsque le contrôle
a la focalisation, le fait qu’un utilisateur tape ‘ROB’ par exemple, provoque la
sélection du premier élément de la boîte liste commençant par ces lettres. S’il
ajoute un ‘E’, le premier élément commençant par les lettres ROBE est
sélectionné, par exemple Robert Johnson. La recherche n’effectue pas de
distinction majuscules/minuscules. Les touches Retour arrière et Echap permettent
d’annuler la recherche de la chaîne (mais laissent la sélection intacte), à l’instar
d’une pause de deux secondes entre deux frappes.

Manipulation de champs booléens avec des cases à cocher


TDBCheckBox est la version orientée données du composant TCheckBox. Vous
pouvez l’utiliser pour définir les valeurs de champs booléens dans un ensemble
de données. Par exemple, un formulaire de facture peut comporter une case qui,
quand elle est cochée, indique que le client est exonéré d’impôt et, quand elle ne
l’est pas, indique qu’il est imposable.
Le composant TDBCheckbox contrôle son propre état (activation ou désactivation)
en comparant le contenu du champ au contenu des propriétés ValueChecked et
ValueUnChecked. Si la valeur de la propriété ValueChecked correspond à la valeur
du champ, le contrôle est activé. Si la valeur de la propriété ValueUnchecked,
correspond à la valeur du champ, le contrôle est désactivé.
Remarque Les valeurs des propriétés ValueChecked et ValueUnchecked ne peuvent pas être
identiques.

26-16 Guide du développeur


Contrôles représentant un champ unique

Donnez à la propriété ValueChecked une valeur que le contrôle doit écrire dans la
base de données s’il est activé lorsque l’utilisateur passe à un autre
enregistrement. Par défaut, cette valeur est à “true”, mais vous pouvez la
changer en valeur alphanumérique en fonction de vos besoins. Vous pouvez
également entrer une liste d’éléments délimités par des points-virgules comme
valeur de ValueChecked. Si l’un de ces éléments correspond au contenu de ce
champ dans l’enregistrement en cours, la case est activée. Par exemple, vous
pouvez spécifier une chaîne ValueChecked comme :
DBCheckBox1.ValueChecked := 'True;Yes;On';
Si le champ de l’enregistrement en cours contient des valeurs “true”, “Yes” ou
“On”, c’est que la case est activée. Aucune distinction majuscules/minuscules
n’est effectuée pendant la comparaison du champ avec les chaînes de
ValueChecked. Si un utilisateur active une case à cocher pour laquelle il y a
plusieurs chaînes ValueChecked, la première est la valeur écrite dans la base de
données.
Donnez à la propriété ValueUnchecked une valeur que le contrôle doit écrire dans
la base de données s’il n’est pas activé lorsque l’utilisateur passe à un autre
enregistrement. Par défaut, cette valeur est définie comme “false”, mais vous
pouvez lui donner n’importe quelle valeur alphanumérique en fonction de vos
besoins. Vous pouvez également entrer une liste d’éléments délimités par des
points-virgules. Si l’un des éléments correspond au contenu de ce champ dans
l’enregistrement en cours, la case à cocher n’est pas activée.
Une case à cocher orientée données est désactivée à chaque fois que le champ de
l’enregistrement en cours ne contient pas l’une des valeurs de la liste des
propriétés ValueChecked ou ValueUnchecked.
Si le champ associé à la case à cocher est un champ logique, elle est toujours
activée lorsque le contenu du champ est à True et désactivée s’il est à False.
Dans ce cas, les chaînes entrées dans les propriétés ValueChecked et
ValueUnchecked sont sans effet sur les champs logiques.

Limitation de valeurs de champ avec des boutons radio


TDBRadioGroup est une version orientée données du composant TRadioGroup. Le
composant TDBRadioGroup vous permet de définir la valeur d’un champ de
données à l’aide d’un bouton radio où le nombre de valeurs possibles est limité.
Le groupe de boutons radio présente un bouton radio pour chaque valeur qu’un
champ peut accepter. Les utilisateurs peuvent définir la valeur d’un champ en
sélectionnant le bouton radio voulu.
La propriété Items détermine le nombre de boutons radio devant figurer dans le
groupe. Items est une liste de chaînes. Un bouton radio est affiché pour chaque
chaîne de la propriété Items et chaque chaîne apparaît à droite du bouton radio
en tant que libellé.

Utilisation de contrôles de données 26-17


Visualisation et édition des données avec un contrôle TDBGrid

Si la valeur actuelle d’un champ associé à un groupe de boutons radio


correspond à l’une des chaînes de la propriété Items, ce bouton radio est
sélectionné. Par exemple, si trois chaînes, “Rouge”, “Jaune” et “Bleu”
apparaissent dans la liste Items et si le champ de l’enregistrement en cours
contient la valeur “Bleu”, le troisième bouton du groupe est sélectionné.
Remarque Si le champ ne correspond à aucune des chaînes de la liste Items, il est toujours
possible de sélectionner un bouton radio lorsque le champ correspond à une
chaîne de la propriété Values. Si le champ de l’enregistrement en cours ne
correspond à aucune des chaînes d’Items ou de Values, aucun bouton radio n’est
sélectionné.
La propriété Values peut contenir une liste facultative de chaînes qu’il est
possible de renvoyer à l’ensemble de données quand l’utilisateur sélectionne un
bouton radio et écrit un enregistrement dans la base de données. Les chaînes
sont associées aux boutons dans un ordre numérique. La première chaîne est
associée au premier bouton, la seconde au second bouton et ainsi de suite. Par
exemple, supposons que la propriété Items contienne “Rouge”, “Jaune” et “Bleu”
et que Values contienne “Magenta”, “Jaune” et “Cyan”. Si un utilisateur
sélectionne le bouton libellé “Rouge”, “Magenta” est écrit dans la base de
données.
Si aucune chaîne n’a été fournie pour Values, la chaîne Item d’un bouton radio
sélectionné est renvoyée à la base de données au moment où un enregistrement
y est écrit.

Visualisation et édition des données avec un contrôle TDBGrid


Le contrôle TDBGrid permet de visualiser et de modifier les enregistrements d’un
ensemble de données dans un format de type grille tabulaire.
Figure 26.1 Contrôle TDBGridl

Champ actif Titres de colonnes

Indicateur
d’enregistrement

Trois facteurs affectent l’aspect des enregistrements affichés dans un contrôle


grille :
• L’existence d’objets colonne persistante définis pour la grille à l’aide de
l’éditeur de colonnes. Les objets colonne persistante offrent une grande
flexibilité pour configurer des grilles et définir l’aspect des données.

26-18 Guide du développeur


Visualisation et édition des données avec un contrôle TDBGrid

• La création de composants champs persistants pour l’ensemble de données


affiché dans la grille. Pour plus d’informations sur la création de ces
composants avec l’éditeur de champs, voir chapitre 19, “Manipulation des
composants champ”.
• La définition du paramètre ObjectView de l’ensemble de données pour
l’affichage des champs ADT et tableau dans la grille. Voir “Affichage des
champs ADT et tableau” à la page 26-25.
Le contrôle grille dispose d’une propriété Columns qui est l’enveloppe d’un objet
TDBGridColumns. TDBGridColumns contient une collection d’objets TColumn
représentant toutes les colonnes du contrôle grille. Vous pouvez utiliser l’éditeur
de colonnes pour configurer les attributs de colonnes lors de la phase de
conception, ou bien utiliser la propriété Columns pour accéder aux méthodes,
événements et propriétés de TDBGridColumns à l’exécution.
La propriété State de la propriété Columns d’une grille indique si des objets
colonne persistante existent. Columns.State est une propriété d’exécution et est
automatiquement définie pour la grille. Sa valeur par défaut est csDefault, ce qui
signifie que les objets colonne persistante n’existent pas. Dans ce cas, l’affichage
des données dans la grille est déterminé par les composants champs persistants
de l’ensemble de données affiché dans la grille ou par les valeurs d’affichage par
défaut dans le cas d’ensembles de données sans composant champ persistant.

Utilisation d’un contrôle grille à son état par défaut


Si la propriété Columns.State d’une grille vaut csDefault, l’aspect des
enregistrements est déterminé principalement par les propriétés des champs de
l’ensemble de données de la grille. Les colonnes des grilles sont générées
dynamiquement à partir des champs visibles de l’ensemble de données, et leur
ordre dans la grille correspond à celui des champs dans l’ensemble de données.
Chaque colonne de la grille est associée à un composant champ. Les
modifications des propriétés des composants champ sont immédiatement
reflétées dans la grille.
Il peut être utile d’utiliser un contrôle grille ayant des colonnes dynamiquement
générées pour afficher et modifier le contenu de certaines tables sélectionnées à
l’exécution. La structure de la grille n’étant pas définie, elle peut changer en temps
réel pour afficher différents ensembles de données. Une grille unique avec des
colonnes dynamiquement générées peut afficher une table Paradox à un certain
moment, puis les résultats d’une requête SQL lorsque sa propriété DataSource est
modifiée, ou lorsque la propriété DataSet de la source des données est modifiée.
L’apparence d’une colonne dynamique peut être modifiée lors de la conception
ou à l’exécution ; dans les deux cas, ce sont les propriétés correspondantes du
composant champ affiché dans la colonne que vous modifiez. La durée des
propriétés des colonnes dynamiques correspond à la période où une colonne est
associée à un champ particulier dans un ensemble de données unique. Par
exemple, la modification de la propriété Width d’une colonne change la propriété
DisplayWidth du champ associé à cette colonne. Les modifications de propriétés
de colonnes non basées sur des propriétés de champs, telles que la propriété
Font, ne sont valides que pendant l’existence de ces colonnes.

Utilisation de contrôles de données 26-19


Visualisation et édition des données avec un contrôle TDBGrid

Les propriétés des colonnes dynamiques sont conservées aussi longtemps que le
composant champ associé existe. Si l’ensemble de données d’une grille consiste
en un ensemble de composants champs dynamiques, les champs sont détruits
dès la fermeture de l’ensemble de données. Lorsque les composants champ sont
détruits, les colonnes qui leur sont associées sont également détruites. Si
l’ensemble de données d’une grille consiste en un ensemble de composants
champs persistants, les composants champs existent même si l’ensemble de
données est fermé, de telle sorte que toutes les colonnes associées à ces champs
gardent leurs propriétés à la fermeture de l’ensemble de données.
Remarque Si la propriété Columns.State d’une grille est mise à csDefault à l’exécution, tous
les objets colonnes de la grille sont supprimés (même les colonnes persistantes)
et les colonnes dynamiques sont reconstruites à partir des champs visibles dans
l’ensemble de données de la grille.

Création d’une grille personnalisée


Un contrôle grille personnalisée est un contrôle pour lequel vous définissez des
objets colonne persistante décrivant l’aspect d’une colonne et la méthode
d’affichage des données dans la colonne. Une grille personnalisée vous permet
de configurer plusieurs grilles pour qu’elles affichent diverses vues d’un même
ensemble de données (divers ordres de colonnes, divers choix de champs et
diverses couleurs et fontes, par exemple). Une grille personnalisée permet aussi
aux utilisateurs de modifier l‘aspect de la grille à l’exécution sans affecter les
champs utilisés par la grille, ou l’ordre des champs de l’ensemble de données.
L’utilisation des grilles personnalisées est conseillée avec les ensembles de
données dont la structure est connue pendant la phase de conception. Comme
elles s’attendent à ce que les noms des champs définis à la conception existent
dans l’ensemble de données, les grilles personnalisées ne sont pas adaptées si
vous devez utiliser des tables arbitraires sélectionnées à l’exécution.

Présentation des colonnes persistantes


Lorsque vous créez des objets colonne persistante pour une grille, ils ne sont
associés aux champs sous-jacents de l’ensemble de données de la grille que de
façon temporaire. Les valeurs par défaut des propriétés des colonnes persistantes
sont lues dynamiquement dans la source par défaut (la grille ou le champ
associé, par exemple) jusqu’à ce qu’une valeur soit affectée à la propriété de la
colonne. Tant que vous n’avez pas affecté une valeur à une propriété de colonne,
sa valeur change si sa source par défaut change.
Par exemple, la source par défaut d’un libellé de colonne est la propriété
DisplayLabel du champ associé. Si vous modifiez la propriété DisplayLabel, le titre
de colonne reflète immédiatement ce changement.
Dès que vous affectez une valeur à une propriété de colonne, elle ne change plus
même si sa source par défaut change. Par exemple, si vous affectez une chaîne
au libellé de la colonne, le titre de la colonne est indépendant de la propriété
DisplayLabel du champ associé. Les modifications de la propriété DisplayLabel du
champ ne sont plus reportées dans le titre de la colonne.

26-20 Guide du développeur


Visualisation et édition des données avec un contrôle TDBGrid

Les colonnes persistantes sont indépendantes des composants champ qui leur
sont associés. De plus, il n’est pas nécessaire d’associer les colonnes persistantes
à des objets champ. Si la propriété FieldName d’une colonne persistante est vide,
ou si le nom du champ ne correspond à aucun champ dans l’ensemble de
données actif dans la grille, la propriété Field de la colonne est NULL et la
colonne est dessinée avec des cellules vides. Vous pouvez utiliser une colonne
vide pour afficher des bitmaps ou des histogrammes représentant les données
d’un enregistrement dans une cellule spécifique, par exemple. Pour ce faire, vous
devez surcharger la méthode de dessin par défaut de la cellule.
Il est possible d’associer une ou plusieurs colonnes persistantes au même champ
d’un ensemble de données. Par exemple, vous pouvez afficher un champ contenant
une référence d’article à droite et à gauche d’une large grille pour faciliter la
recherche d’une référence en évitant à l’utilisateur de faire défiler la grille.
Remarque Etant donné que les colonnes persistantes n’ont pas besoin d’être associées à un
champ d’un ensemble de données, et étant donné que plusieurs colonnes
peuvent référencer le même champ, la valeur de la propriété FieldCount d’une
grille personnalisée peut être inférieure ou égale au nombre de colonnes d’une
grille. Notez également que si la colonne sélectionnée dans la grille personnalisée
n’est pas associée à un champ, la propriété SelectedField de la grille est NULL et
la propriété SelectedIndex est à –1.
Les colonnes persistantes peuvent être configurées pour afficher des cellules de
grille sous forme de liste déroulante dans une boîte à options de valeurs de
référence provenant d’un autre ensemble de données ou d’une liste de choix
statique, ou sous forme d’un bouton à points de suspension (…) dans une
cellule, sur laquelle l’utilisateur peut cliquer pour lancer des visionneurs de
données spéciaux ou des boîtes de dialogue en relation avec la cellule en cours.

Détermination de la source d’une propriété de colonne à l’exécution


A l’exécution, vous pouvez tester la propriété AssignedValues d’une colonne pour
savoir si une propriété de colonne obtient sa valeur d’un composant champ
associé ou si elle possède sa propre valeur.
Il est possible de redéfinir toutes les propriétés par défaut d’une colonne en
appelant la méthode RestoreDefaults de cette colonne. Vous pouvez aussi redéfinir
les propriétés par défaut de toutes les colonnes d’une grille en appelant la
méthode RestoreDefaults de la liste de colonnes :
DBGrid1.Columns.RestoreDefaults;
Pour ajouter une colonne persistante, appelez la méthode Add de la liste de
colonnes :
DBGrid1.Columns.Add;
Vous pouvez supprimer une colonne persistante en libérant l’objet colonne :
DBGrid1.Columns[5].Free;
Pour finir, l’affectation à l’exécution de csCustomized à la propriété Column.State
d’une grille place la grille dans un mode personnalisé. Toutes les colonnes
existantes de la grille sont détruites et de nouvelles colonnes persistantes sont
construites pour chaque champ de l’ensemble de données de la grille.

Utilisation de contrôles de données 26-21


Visualisation et édition des données avec un contrôle TDBGrid

Création de colonnes persistantes


Pour personnaliser l’aspect d’une grille lors de la phase de conception, appelez
l’éditeur de colonnes pour créer un ensemble d’objets colonne persistante pour la
grille. La propriété State d’une grille comportant des objets colonne persistante
est automatiquement mise à csCustomized.
Pour créer des colonnes persistantes pour un contrôle de grille,
1 Sélectionnez le composant grille dans la fiche.
2 Appelez l’éditeur de colonnes en double-cliquant sur la propriété Columns de
la grille dans l’inspecteur d’objets.
La boîte liste des colonnes affiche les colonnes persistantes définies pour la grille
sélectionnée. Lorsque vous ouvrez l’éditeur de colonnes pour la première fois,
cette liste est vide car la grille est dans son mode par défaut et ne contient que
des colonnes dynamiques.
Vous pouvez créer des colonnes pour tous les champs d’un ensemble de données
en une seule fois, ou vous pouvez créer des colonnes persistantes sur une base
individuelle. Pour créer des colonnes persistantes pour tous les champs :
1 Cliquez avec le bouton droit de la souris pour appeler le menu contextuel et
choisissez la commande Ajouter tous les champs. Si la grille n’est associée à
aucune source de données, Ajouter tous les champs est désactivée. Associez la
grille avec une source de données ayant un ensemble de données actif avant
de choisir cette commande.
2 Si la grille contient déjà des colonnes persistantes, une boîte de dialogue vous
demande si vous souhaitez supprimer les colonnes existantes. Si vous
répondez Oui, toutes les informations existantes sur les champs persistants
sont supprimées et les champs de l’ensemble de données en cours sont insérés
selon leur ordre dans l’ensemble de données. Si vous répondez Non, les
champs persistants existants restent intacts, et les nouvelles informations de
colonnes basées sur les champs supplémentaires de l’ensemble de données
sont ajoutées à l’ensemble de données.
3 Cliquez sur la case de fermeture pour appliquer les colonnes persistantes à la
grille et fermer la boîte de dialogue.
Pour créer des colonnes persistantes individuelles :
1 Choisissez le bouton Ajouter dans l’éditeur de colonnes. La nouvelle colonne
sera sélectionnée dans la boîte liste. Elle porte un numéro et un nom par
défaut (comme, 0 - TColumn).
2 Pour associer un champ à cette nouvelle colonne, définissez la propriété
FieldName dans l’inspecteur d’objets.
3 Pour définir le titre de la nouvelle colonne, définissez l’option Caption de la
propriété Title dans l’inspecteur d’objets.
4 Fermez l’éditeur de colonnes pour appliquer les colonnes persistantes à la
grille et fermer la boîte de dialogue.

26-22 Guide du développeur


Visualisation et édition des données avec un contrôle TDBGrid

Suppression de colonnes persistantes


Il peut être utile de supprimer une colonne persistante d’une grille pour éliminer
les champs que vous ne souhaitez pas afficher. Pour supprimer une colonne
persistante :
1 Sélectionnez le champ à supprimer dans la boîte liste des colonnes.
2 Cliquez sur Supprimer (vous pouvez aussi utiliser le menu contextuel ou la
touche Suppr).
Remarque Si vous supprimez toutes les colonnes d’une grille, la propriété Columns.State
revient à l’état csDefault et crée automatiquement des colonnes dynamiques pour
chaque champ de l’ensemble de données.

Modification de l’ordre des colonnes persistantes


Les colonnes apparaissent dans l‘éditeur de colonnes dans le même ordre que
dans la grille. Vous pouvez modifier l’ordre des colonnes en faisant un glisser-
déplacer depuis la boîte liste des colonnes.
Pour modifier la position d’une colonne :
1 Sélectionnez la colonne dans la boîte liste des colonnes.
2 Faites-la glisser vers son nouvel emplacement dans la boîte liste.
Vous pouvez aussi faire glisser les colonnes dans la grille elle-même, comme
vous le feriez à l’exécution.

Définition d’une colonne de liste de référence


Si vous voulez qu’une colonne affiche une liste déroulante de valeurs extraites
d’une table de référence distincte, vous devez définir un objet champ de
référence dans l’ensemble de données. Pour plus d’informations, voir “Définition
d’un champ de référence” à la page 19-11.
Lorsque le champ de référence est défini, affectez son nom à la propriété
FieldName de la colonne, et vérifiez que la propriété ButtonStyle de la colonne est
à cbsAuto. La grille affiche alors automatiquement un bouton de boîte à options
lorsqu’une cellule de cette colonne est en mode Edition. La liste déroulante
contient les valeurs de référence définies dans le champ de référence.

Définition d’une colonne de liste de choix


Une colonne de liste de choix apparaît et fonctionne comme une colonne de liste
de référence. La seule différence étant que le champ de la colonne est un champ
normal et que la liste déroulante contient la liste des valeurs de la propriété
PickList de la colonne au lieu d’un champ de table de référence.
Pour définir une colonne de liste de choix :
1 Sélectionnez une colonne dans la boîte liste des colonnes.
2 Choisissez la valeur cbsAuto dans la boîte de la propriété ButtonStyle.
3 Double-cliquez sur la propriété PickList dans l’inspecteur d’objets pour afficher
l’éditeur de liste de chaînes.

Utilisation de contrôles de données 26-23


Visualisation et édition des données avec un contrôle TDBGrid

Dans cet éditeur, entrez les valeurs devant apparaître dans la liste déroulante
pour la colonne sélectionnée. Si la liste de choix contient des données, la colonne
devient une colonne de liste de choix.
Remarque Pour restaurer le comportement par défaut d’une colonne, supprimez tout le
texte de l’éditeur de liste de chaînes.

Insertion d’un bouton dans une colonne


Une colonne peut contenir un bouton à points de suspension (…), placé à droite
de l’éditeur de cellules standard. En cliquant sur la souris ou en appuyant sur
Ctrl+Entrée, vous déclenchez l’événement OnEditButtonClick de la grille. Vous
pouvez utiliser le bouton à points de suspension pour ouvrir des fiches
contenant des vues plus détaillées des données de la colonne. Par exemple, dans
une table affichant la synthèse des factures émises, vous pouvez intégrer un
bouton à points de suspension dans la colonne des totaux permettant d’afficher
une fiche contenant les éléments d’une facture précise ou le montant de la TVA,
etc. Pour les champs graphiques, vous pouvez utiliser ce bouton pour afficher
une fiche contenant l’image.
Pour doter une colonne d’un bouton à points de suspension :
1 Sélectionnez la colonne dans la boîte liste des colonnes.
2 Choisissez cbsEllipsis comme valeur de la propriété ButtonStyle.
3 Ecrivez un gestionnaire d’événement OnEditButtonClick.

Définition des propriétés de colonne en mode conception


Les propriétés d’une colonne déterminent l’affichage des données dans ses
cellules. La plupart des propriétés héritent leur valeur par défaut des propriétés
associées à un composant distinct, appelé la source par défaut. Il peut s’agir d’une
grille ou d’un composant champ associé.
Pour définir les propriétés d’une colonne, sélectionnez la colonne dans l’éditeur
de colonnes et définissez ses propriétés dans l’inspecteur d’objets. Le tableau
suivant dresse la liste des propriétés de colonne pouvant être définies.

Tableau 26.5 Propriétés de colonne


Propriété Fonction
Alignment Aligne (à gauche ou à droite) ou centre les données du champ dans la
colonne. Source par défaut : TField.Alignment.
ButtonStyle cbsAuto : (par défaut) affiche une liste déroulante si le champ associé est
un champ de référence, ou si la propriété PickList de la colonne contient
des données.
cbsEllipsis : affiche un bouton à points de suspension (...) à droite de la cellule.
Un clic sur ce bouton déclenche l’événement OnEditButtonClick de la grille.
cbsNone : la colonne utilise le contrôle de saisie normal pour modifier les
données contenues dans la colonne.
Color Spécifie la couleur de fond des cellules de la colonne. Pour définir la
couleur de fond du texte, utilisez la propriété Font. Source par défaut :
TDBGrid.Color.

26-24 Guide du développeur


Visualisation et édition des données avec un contrôle TDBGrid

Tableau 26.5 Propriétés de colonne (suite)


Propriété Fonction
DropDownRows Nombre de lignes de texte affichées dans la liste déroulante (7 par défaut).
Expanded Spécifie si la colonne est développée. S’applique uniquement aux colonnes
représentant des champs ADT ou tableau.
FieldName Spécifie le nom du champ associé à cette colonne (peut être vierge).
ReadOnly True : les données dans la colonne ne sont pas modifiables par l’utilisateur.
False : (valeur par défaut) les données dans la colonne sont modifiables.
Width Spécifie la largeur de la colonne en pixels. La source par défaut vient de
TField.DisplayWidth.
Font Spécifie la fonte, la taille et la couleur du texte dans la colonne. Source par
défaut : TDBGrid.Font.
PickList Contient une liste de valeurs à afficher dans une liste déroulante dans la
colonne.
Title Définit les propriétés du titre de la colonne sélectionnée.

Le tableau ci-dessous résume les propriétés pouvant être définies pour la


propriété Title.

Tableau 26.6 Propriétés disponibles pour la propriété Title


Propriété Fonction
Alignment Aligne à gauche (défaut), à droite, ou centre le libellé dans le titre de colonne.
Caption Spécifie le texte à afficher dans le titre de la colonne. Source par défaut :
TField.DisplayLabel.
Color Spécifie la couleur de fond de la cellule de titre de colonne. Source par défaut :
TDBGrid.FixedColor.
Font Spécifie la fonte, la taille et la couleur du texte dans le titre de la colonne.
Source par défaut : TDBGrid.TitleFont.

Restauration des valeurs par défaut d’une colonne


Vous pouvez annuler les modifications de propriétés apportées à une ou
plusieurs colonnes. Dans l’éditeur de colonnes, sélectionnez la ou les colonnes à
restaurer, puis choisissez Restaurer défauts dans le menu contextuel. Cette
commande provoque l’annulation des paramètres de propriétés définis et
restaure les propriétés d’une colonne aux valeurs des propriétés dérivées du
composant champ sous-jacent.

Affichage des champs ADT et tableau


En fonction de la valeur de la propriété ObjectView de l’ensemble de données,
une grille affiche les champs ADT et tableau soit en mode aplani, soit dans un
mode objet qui permet de les développer ou de les réduire. Lorsque ObjectView
vaut True, les champs objet peuvent être développés et réduits. Lorsqu’un champ
est développé, chaque champ enfant apparaît dans sa propre colonne avec une
barre de titre, en dessous de la barre de titre du champ ADT ou tableau.

Utilisation de contrôles de données 26-25


Visualisation et édition des données avec un contrôle TDBGrid

Lorsque le champ est réduit, seule la colonne apparaît avec une chaîne non
modifiable des champs enfants, séparés par des virgules. Une colonne peut être
étendue et réduite en cliquant sur la flèche dans la barre de titre du champ et en
définissant la propriété Expanded de la colonne. Lorsque la propriété ObjectView
de l’ensemble de données vaut False, chaque champ enfant apparaît dans une
colonne séparée.

Tableau 26.7 Propriétés relatives à l’affichage des champs ADT et tableau dans un composant TDBGrid
Propriété Objet Fonction
Expandable TColumn Spécifie si la colonne peut être étendue pour afficher les
champs enfants dans des colonnes séparées et modifiables.
Expanded TColumn Spécifie si la colonne est étendue.
MaxTitleRows TDBGrid Spécifie le nombre maximum de lignes de titre pouvant
apparaître dans la grille.
ObjectView TDataSet Spécifie si les champs apparaissent aplanis, ou dans un mode
objet, où chaque champ objet peut être développé et réduit.
ParentColumn TColumn Fait référence à l’objet TColumn auquel appartient la
colonne du champ enfant.

La figure 26.2 montre la grille avec un champ ADT et un champ tableau. La


propriété ObjectView de l’ensemble de données vaut False de sorte que chaque
champ enfant possède une colonne.
Figure 26.2 Contrôle TDBGrid avec ObjectView valant False
Champs enfant ADT Champs enfant tableau

Les figures 26.3 et 26.4 montrent la grille avec un champ ADT et un champ
tableau. La figure 26.3 montre les champs réduits, état dans lequel ils ne peuvent
pas être modifiés. La figure 26.4 montre les champs développés. Les champs sont
développés et réduits en cliquant sur la flèche dans leur barre de titre.
Figure 26.3 Contrôle TDBGrid avec Expanded valant False

26-26 Guide du développeur


Visualisation et édition des données avec un contrôle TDBGrid

Figure 26.4 Contrôle TDBGrid avec Expanded valant True


Colonnes de champ enfant ADT Colonnes de champ enfant tableau

Définition des options de la grille


La propriété Options d’une grille permet de contrôler, à la conception, le
comportement et l’aspect de la grille à l’exécution. Quand un composant grille
est placé dans une fiche à la conception, la propriété Options de l’inspecteur
d’objets apparaît avec un signe + (plus) indiquant qu’elle peut être développée
pour afficher une série de propriétés booléennes définies individuellement.
Pour visualiser et définir ces propriétés, double-cliquez sur la propriété Options.
La liste des options apparaît dans l’inspecteur d’objets sous la propriété Options.
Le signe + devient un signe - (moins), indiquant que la liste des propriétés peut
être refermée par un double-clic sur la propriété Options.
Le tableau suivant liste les propriétés Options pouvant être définies et décrit leur
effet sur la grille à l’exécution.
Tableau 26.8 Options détaillées des propriétés Options du composant TDBGrid
Option Effet
dgEditing True : (valeur par défaut) autorise la modification, l’insertion et la
suppression d’enregistrements dans la grille.
False : interdit la modification, l’insertion et la suppression
d’enregistrements dans la grille.
dgAlwaysShowEditor True : quand un champ est sélectionné, il est en mode Edition.
False : (valeur par défaut) un champ n’est pas automatiquement en
mode Edition s’il est sélectionné.
dgTitles True : (valeur par défaut) affiche les noms de champs en haut de la
grille.
False : l’affichage des noms de champs est désactivé.
dgIndicator True : (valeur par défaut) la colonne d’indicateur est affichée sur la
gauche de la grille et l’indicateur d’enregistrement en cours (une
flèche à gauche de la grille) est activé. A l’insertion, la flèche se
transforme en astérisque et se transforme en I à la modification.
False : la colonne d’indicateur n’apparaît pas.
dgColumnResize True : (valeur par défaut) les colonnes peuvent être
redimensionnées en faisant glisser les lignes de colonnes dans la
zone de titre. Le redimensionnement modifie la largeur
correspondante du composant TField sous-jacent.
False : les colonnes de la grille ne peuvent être redimensionnées.

Utilisation de contrôles de données 26-27


Visualisation et édition des données avec un contrôle TDBGrid

Tableau 26.8 Options détaillées des propriétés Options du composant TDBGrid (suite)
Option Effet
dgColLines True : (valeur par défaut) affiche une ligne verticale de séparation
entre les colonnes.
False : n’affiche pas de ligne verticale de séparation entre les
colonnes.
dgRowLines True : (valeur par défaut) affiche une ligne horizontale de
séparation entre les enregistrements.
False : n’affiche pas de ligne horizontale de séparation entre les
enregistrements.
dgTabs True : (valeur par défaut) autorise la tabulation entre les champs
des enregistrements.
False : la tabulation fait sortir du contrôle grille.
dgRowSelect True : la barre de sélection occupe toute la largeur de la grille.
False : (valeur par défaut) la sélection d’un champ d’un
enregistrement ne sélectionne que ce champ.
dgAlwaysShowSelection True : (valeur par défaut) la barre de sélection de la grille est
toujours visible, même si un autre contrôle a la focalisation.
False : la barre de sélection n’apparaît dans la grille que si la grille
a la focalisation.
dgConfirmDelete True : (valeur par défaut) demande la confirmation de la
suppression d’enregistrements (Ctrl+Suppr).
False : supprime les enregistrements sans confirmation.
dgCancelOnExit True : (valeur par défaut) annule une insertion en attente lorsque la
focalisation quitte la grille. Cela permet d’éviter des validations
involontaires d’enregistrements vierges ou partiellement vierges.
False : permet à une insertion en attente d’être validée.
dgMultiSelect True : permet aux utilisateurs de sélectionner des lignes non
contiguës en utilisant les touches Ctrl+Maj ou Maj+flèche.
False : (valeur par défaut) ne permet pas à l’utilisateur de
sélectionner plusieurs lignes.

Saisie de modifications dans la grille


Lors de l’exécution, vous pouvez utiliser une grille pour modifier les données
existantes et pour entrer de nouveaux enregistrements si :
• La propriété CanModify de l’ensemble de données est à True.
• La propriété ReadOnly de la grille est à False.
Lorsqu’un utilisateur modifie un enregistrement de la grille, les modifications
apportées à chaque champ sont émises dans le tampon interne des
enregistrements, et ne sont pas émises (ou validées) tant que l’utilisateur n’est
pas passé à un autre enregistrement de la grille. Même si vous utilisez la souris
pour déplacer la focalisation sur un autre contrôle de la fiche, la grille n’écrit vos
modifications que lorsque vous changez d’enregistrement en cours. Lorsqu’un
enregistrement est écrit, l’ensemble de données vérifie si le statut des composants

26-28 Guide du développeur


Visualisation et édition des données avec un contrôle TDBGrid

orientés données associés à l’ensemble de données a été modifié. En cas de


problème de mise à jour d’un champ contenant des données modifiées, Delphi
provoque une exception et ne modifie pas l’enregistrement.
Vous pouvez annuler toutes les modifications d’un enregistrement en appuyant
sur Echap dans un champ avant d’activer un autre enregistrement.

Changement de l’ordre des colonnes à la conception


Dans les contrôles grille ayant des colonnes persistantes, et dans les grilles par
défaut dont les ensembles de données contiennent des champs persistants, vous
pouvez déplacer les colonnes à la conception en cliquant sur leur cellule de titre
et en la faisant glisser.
Remarque Le déplacement des champs persistants dans l’éditeur de champs a pour effet de
déplacer les colonnes dans une grille par défaut mais pas dans une grille
personnalisée.
Important Vous ne pouvez pas, en phase de conception, déplacer des colonnes dans les
grilles contenant des colonnes et des champs dynamiques, car aucun élément
persistant ne peut enregistrer la modification.

Changement de l’ordre des colonnes à l’exécution


L’utilisateur peut, à l’exécution, utiliser la souris pour faire glisser une colonne à
un nouvel emplacement de la grille si sa propriétéDragMode vaut dmManual. Le
changement de l’ordre des colonnes d’une grille pour lesquelles la propriété State
vaut csDefault, provoque également la modification de l’ordre des composants
champ dans l’ensemble de données sous-jacent. L’ordre des champs dans la table
elle-même n’est pas affecté.
L’événement OnColumnMoved d’une grille est déclenché après le déplacement
d’une colonne.
Pour empêcher le changement de l’ordre des colonnes, il faut mettre la propriété
DragMode à dmAutomatic.

Contrôle du dessin de la grille


Le premier niveau de contrôle du dessin des cellules de la grille consiste à
définir les propriétés des colonnes. La grille utilise par défaut la fonte,
l’alignement et la couleur d’une colonne pour dessiner les cellules de cette
colonne. Le texte des champs de données est dessiné à l’aide des propriétés
DisplayFormat et EditFormat du composant champ associé à la colonne.
Vous pouvez modifier la logique d’affichage de la grille en entrant du code dans
l’événement OnDrawColumnCell d’une grille. Si la propriété DefaultDrawing de la
grille est à True, le dessin normal est effectué avant que votre événement
OnDrawColumnCell ne soit appelé. Votre code peut alors se superposer à l’affichage
par défaut. Cette fonctionnalité vous sera utile si vous avez défini une colonne
persistante vierge et désirez ajouter des dessins dans les cellules de cette colonne.

Utilisation de contrôles de données 26-29


Visualisation et édition des données avec un contrôle TDBGrid

Si vous souhaitez remplacer la logique de dessin de la grille, mettez


DefaultDrawing à False et placez le code dans l’événement OnDrawColumnCell de
la grille. Si vous désirez remplacer la logique de dessin pour certaines colonnes
ou certains types de données, vous pouvez appeler DefaultDrawColumnCell
depuis votre gestionnaire d’événement OnDrawColumnCell pour que la grille
utilise son code de dessin normal pour les colonnes sélectionnées. Ceci réduit
votre travail si vous ne voulez modifier que la façon dont les champs logiques
sont dessinés, par exemple.

Comment répondre aux actions de l’utilisateur à l’exécution


Le comportement de la grille peut être modifié en écrivant des gestionnaires
d’événements répondant à des actions spécifiques dans la grille. Une grille
affichant en général plusieurs champs et enregistrements de façon simultanée,
des besoins très spécifiques peuvent intervenir dans la réponse à des
modifications de colonnes. Il est possible, par exemple, d’activer ou de désactiver
un bouton de la fiche à chaque fois que l’utilisateur entre ou sort d’une colonne
particulière.
Le tableau suivant liste les événements d’un contrôle grille disponibles dans
l’inspecteur d’objets.

Tableau 26.9 Evénements d’un contrôle grille


Evénement Utilisation
OnCellClick Spécifie l’action à lancer lorsqu’un utilisateur clique sur une cellule de
la grille.
OnColEnter Spécifie l’action à lancer lorsque l’utilisateur se place dans une
colonne de la grille.
OnColExit Spécifie l’action à lancer lorsque l’utilisateur quitte une colonne de la
grille.
OnColumnMoved Appelé lorsque l’utilisateur déplace une colonne.
OnDblClick Spécifie l’action à lancer lorsque l’utilisateur double-clique dans la
grille.
OnDragDrop Spécifie l’action à lancer lorsque l’utilisateur se sert du glisser-déplacer
dans la grille.
OnDragOver Spécifie l’action à lancer lorsque l’utilisateur fait glisser la sélection sur
la grille.
OnDrawColumnCell Appelé pour dessiner des cellules individuelles.
OnDrawDataCell (obsolète) Dans les grilles pour lesquelles State = csDefault, appelé
pour dessiner des cellules individuelles.
OnEditButtonClick Appelé quand l’utilisateur clique sur un bouton à points de
suspension (...) dans une colonne.
OnEndDrag Spécifie l’action à lancer lorsque l’utilisateur arrête de faire glisser la
sélection sur la grille.
OnEnter Spécifie l’action à lancer lorsque la grille reçoit la focalisation.
OnExit Spécifie l’action à lancer lorsque la grille perd la focalisation.

26-30 Guide du développeur


Création d’une grille contenant d’autres contrôles orientés données

Tableau 26.9 Evénements d’un contrôle grille (suite)


Evénement Utilisation
OnKeyDown Spécifie l’action à lancer lorsque l’utilisateur appuie sur une touche ou
une combinaison de touches du clavier dans la grille.
OnKeyPress Spécifie l’action à lancer lorsque l’utilisateur appuie sur une touche
alphanumérique du clavier dans la grille.
OnKeyUp Spécifie l’action à lancer lorsque l’utilisateur relâche une touche du
clavier dans la grille.
OnStartDrag Spécifie l’action à lancer lorsque l’utilisateur démarre un glisser-
déplacer sur la grille.
OnTitleClick Spécifiez l’action à lancer lorsqu’un utilisateur clique sur le titre d’une
colonne.

Il y a de nombreuses utilisations possibles pour ces événements. Par exemple, il


est possible d’écrire pour l’événement OnDblClick, un gestionnaire qui fasse
apparaître une liste surgissante dans laquelle l’utilisateur peut choisir la valeur à
entrer dans la colonne. Un tel gestionnaire utiliserait la propriété SelectedField
pour déterminer la ligne et la colonne actives.

Création d’une grille contenant d’autres contrôles orientés


données
Un contrôle TDBCtrlGrid permet d’afficher plusieurs champs de plusieurs
enregistrements dans un format grille tabulaire. Chaque cellule de la grille
affiche plusieurs champs d’une ligne. Pour utiliser une grille de contrôle de base
de données :
1 Placez une grille de contrôle de base de données sur la fiche.
2 Donnez à la propriété DataSource de la grille le nom de la source de données.
3 Placez des contrôles de données dans la cellule de conception de la grille (la
cellule qui se trouve le plus haut ou le plus à gauche de la grille, la seule
dans laquelle vous puissiez placer des contrôles).
4 Donnez à la propriété DataField de chaque contrôle de données le nom d’un
champ. La source de données de ces contrôles est déjà définie comme la
source de la grille de contrôle de base de données.
5 Disposez les contrôles dans la cellule comme vous l’entendez.
Lorsque vous compilez et exécutez une application contenant une grille de
contrôle de base de données, la disposition des contrôles orientés données dans
la cellule de conception (définie à l’exécution) est dupliquée dans chaque cellule
de la grille. Chaque cellule affiche un enregistrement distinct d’un ensemble de
données.

Utilisation de contrôles de données 26-31


Navigation et manipulation d’enregistrements

Figure 26.5 TDBCtrlGrid en mode conception

Le tableau ci-dessous résume les propriétés uniques des grilles de contrôle de


base de données que vous pouvez définir en phase de conception :
Tableau 26.10 Propriétés d’une grille de contrôle de base de données
Propriété Fonction
AllowDelete True (valeur par défaut) : permet la suppression des enregistrements.
False : interdit la suppression des enregistrements.
AllowInsert True (valeur par défaut) : permet l’insertion d’enregistrements.
False : interdit l’insertion d’enregistrements.
ColCount Définit le nombre de colonnes dans la grille (1 par défaut).
Orientation goVertical (valeur par défaut) : affiche les enregistrements de haut en bas.
goHorizontal : affiche les enregistrements de gauche à droite.
PanelHeight Définit la hauteur d’un volet (72 par défaut).
PanelWidth Définit la largeur d’un volet (200 par défaut).
RowCount Définit le nombre de volets à afficher (3 par défaut).
ShowFocus True (valeur par défaut) : affiche, à l’exécution, un rectangle de focalisation
autour du volet de l’enregistrement en cours.
False : n’affiche pas de rectangle de focalisation.

Pour plus d’informations sur les propriétés et les méthodes des grilles de
contrôle de base de données, voir la référence VCL en ligne.

Navigation et manipulation d’enregistrements


TDBNavigator est un contrôle simple permettant à l’utilisateur de naviguer parmi
les enregistrements d’un ensemble de données et de les manipuler. Le navigateur
se compose d’une série de boutons permettant à l’utilisateur de faire défiler vers
l’avant ou vers l’arrière des enregistrements, un par un, d’aller directement au
premier ou au dernier, d’insérer un nouvel enregistrement, de mettre à jour un
enregistrement existant, d’écrire des modifications ou d’en annuler, de supprimer
un enregistrement ou de rafraîchir l’affichage.

26-32 Guide du développeur


Navigation et manipulation d’enregistrements

La figure 26.6 montre le navigateur tel qu’il apparaît par défaut lorsque vous le
placez sur une fiche lors de la conception. Le navigateur se compose d’une série
de boutons permettant à l’utilisateur de naviguer d’un enregistrement à l’autre
dans un ensemble de données, et de modifier, supprimer, insérer et valider des
enregistrements. La propriété VisibleButtons du navigateur vous permet d’afficher
ou de cacher dynamiquement un sous-ensemble de ces boutons.
Figure 26.6 Boutons de TDBNavigatorl
Ins. enreg. Suppr. enreg. actif
Enreg. suiv. Ecriture édition

1er enreg. Rafraîchissement

Enreg. préc. Annulation d’édition


Dernier enr. Edit. enreg. actif

Le tableau ci-dessous donne la description des boutons du navigateur.


Tableau 26.11 Boutons de TDBNavigator
Boutons Fonction
Premier Fait appel à la méthode First de l’ensemble de données pour que le premier
enregistrement de l’ensemble de données devienne l’enregistrement en cours.
Précédent Fait appel à la méthode Prior de l’ensemble de données pour que
l’enregistrement précédent devienne l’enregistrement en cours.
Suivant Fait appel à la méthode Next de l’ensemble de données pour que
l’enregistrement suivant devienne l’enregistrement en cours.
Dernier Fait appel à la méthode Last de l’ensemble de données pour que le dernier
enregistrement devienne l’enregistrement en cours.
Insertion Fait appel à la méthode Insert de l’ensemble de données pour insérer un
nouvel enregistrement avant l’enregistrement en cours et placer l’ensemble de
données en mode Insertion.
Suppression Supprime l’enregistrement en cours. Si la propriété ConfirmDelete est à True, il
est demandé confirmation avant la suppression.
Edition Place l’ensemble de données en mode Edition afin de pouvoir modifier
l’enregistrement en cours.
Ecriture Ecrit les modifications dans l’enregistrement en cours dans la base de données.
Annulation Annule l’édition de l’enregistrement en cours, et replace l’ensemble de
données à l’état Visualisation.
Rafraîchisse Vide les tampons d’affichage du contrôle orienté données, puis les rafraîchit à
ment partir de la table ou de la requête physique. Utile si les données sous-jacentes
ont pu être modifiées par une autre application.

Choix des boutons visibles


Lorsque vous placez un composant TDBNavigator sur une fiche, tous ses boutons
sont visibles par défaut. Vous pouvez utiliser la propriété VisibleButtons pour les
désactiver si vous ne comptez pas les utiliser sur une fiche. Par exemple, il peut
être utile de désactiver les boutons Edition, Insertion, Suppression, Ecriture et
Annuler sur une fiche servant à parcourir des enregistrements plutôt qu’à les éditer.

Utilisation de contrôles de données 26-33


Navigation et manipulation d’enregistrements

Affichage et dissimulation des boutons en mode conception


La propriété VisibleButtons de l’inspecteur d’objets est suivie du signe + pour
indiquer que sa liste d’options peut être développée. Vous pouvez choisir
d’afficher une valeur booléenne pour chacun des boutons du navigateur. Pour
voir ces valeurs et les définir, double-cliquez sur la propriété VisibleButtons. La
liste des boutons pouvant être activés ou désactivés apparaît dans l’inspecteur
d’objets en dessous de la propriété VisibleButtons. Le signe + devient - (moins),
indiquant que vous pouvez réduire la liste des propriétés par un double-clic sur
la propriété VisibleButtons.
La visibilité d’un bouton est indiquée par l’état Boolean de sa valeur. Si cette valeur
est à True, le bouton apparaît dans le composant TDBNavigator. Si elle est à False,
le bouton est supprimé du navigateur lors de la conception et de l’exécution.
Remarque Quand la valeur d’un bouton est à False, il est supprimé du composant
TDBNavigator et tous les autres boutons s’agrandissent pour occuper toute la
largeur du contrôle. Vous pouvez faire glisser les poignées de celui-ci pour
redimensionner les boutons.
Pour en savoir plus sur les boutons et les méthodes auxquelles ils font appel,
voir la référence VCL en ligne.

Affichage et dissimulation des boutons à l’exécution


Lors de l’exécution, vous avez la possibilité de masquer ou d’afficher les boutons
du navigateur en réponse aux actions de l’utilisateur ou aux états de
l’application. Supposons, par exemple, que vous fournissiez un seul navigateur
pour consulter deux ensembles de données, l’un permettant aux utilisateurs
d’éditer les enregistrements, l’autre étant en lecture seulement. Lorsque vous
passez de l’un à l’autre, vous devez masquer les boutons Insertion, Suppression,
Edition, Ecriture, Annulation et Rafraîchissement pour le second ensemble de
données et les afficher pour le premier.
Supposons que vous vouliez empêcher toute édition d’OrdersTable en masquant
les boutons Insertion, Suppression, Edition, Ecriture, Annulation et
Rafraîchissement du navigateur, mais que vous vouliez permettre en même
temps l’édition de CustomersTable. La propriété VisibleButtons détermine quels
sont les boutons affichés dans le navigateur. Voici comment ajouter le code
nécessaire au gestionnaire d’événement OnEnter codé préalablement :
procedure TForm1.CustomerCompanyEnter(Sender :TObject);
begin
if Sender = CustomerCompany then
begin
DBNavigatorAll.DataSource := CustomerCompany.DataSource;
DBNavigatorAll.VisibleButtons := [nbFirst,nbPrior,nbNext,nbLast];
end
else
begin
DBNavigatorAll.DataSource := OrderNum.DataSource;
DBNavigatorAll.VisibleButtons := DBNavigatorAll.VisibleButtons + [nbInsert,
nbDelete,nbEdit,nbPost,nbCancel,nbRefresh];
end;
end;

26-34 Guide du développeur


Navigation et manipulation d’enregistrements

Affichage de panneaux d’information


Pour afficher un panneau d’information pour chaque bouton du navigateur à
l’exécution, attribuez à la propriété ShowHint la valeur True. Le navigateur
affichera alors des panneaux d’information lorsque vous ferez passer le curseur
sur les boutons. Par défaut ShowHint est à False .
La propriété Hints contrôle le texte des panneaux d’information des boutons. Par
défaut, Hints est une liste de chaînes vide. Quand elle est vide, un texte d’aide
s’affiche par défaut pour chaque bouton. Pour personnaliser ces panneaux
d’information, utilisez l’éditeur de liste de chaînes et saisissez une ligne de texte
distincte pour chaque bouton. Lorsqu’elles sont présentes, ces chaînes ont priorité
sur les chaînes par défaut du navigateur.

Utilisation d’un navigateur pour plusieurs ensembles de données


Comme pour les autres contrôles orientés données, la propriété DataSource d’un
navigateur spécifie la source de données qui lie le contrôle à un ensemble de
données. En changeant la propriété DataSource d’un navigateur lors de
l’exécution, vous pouvez faire en sorte qu’un même navigateur procure des
fonctions de navigation pour plusieurs ensembles de données.
Supposons qu’une fiche contienne deux contrôles DBEdit liés aux ensembles de
données CustomerTable et OrdersTable via respectivement les sources de données
CustomersSource et OrdersSource. Lorsqu’un utilisateur accède au contrôle DBEdit
connecté à CustomersSource (CustomerCompany), le navigateur doit également
utiliser CustomersSource ; il en va de même pour OrdersSource (OrderNum). Vous
pouvez coder un gestionnaire d’événement OnEnter pour l’un des contrôles
DBEdit, puis le partager avec l’autre. Exemple :
procedure TForm1.CustomerCompanyEnter(Sender :TObject);
begin
if Sender = CustomerCompany then
DBNavigatorAll.DataSource := CustomerCompany.DataSource
else
DBNavigatorAll.DataSource := OrderNum.DataSource;
end;

Utilisation de contrôles de données 26-35


26-36 Guide du développeur
Chapitre

Utilisation de composants d’aide


Chapter 27
27
à la décision
Les composants d’aide à la décision permettent de créer des graphes et des
tableaux de références croisées pour visualiser et analyser des données selon
différentes perspectives. Pour plus d’informations sur les références croisées, voir
“Présentation des références croisées” à la page 27-2.

Présentation
Les composants d’aide à la décision apparaissent sur la page Decision Cube de
la palette des composants :
• Le cube de décision, TDecisionCube, est un lieu de stockage de données
multidimensionnelles.
• La source de décision, TDecisionSource, définit l’état actuel du pivot d’une
grille ou d’un graphe de décision.
• La requête de décision, TDecisionQuery, est une forme spécialisée de TQuery
utilisée pour définir les données d’un cube de décision.
• Le pivot de décision, TDecisionPivot, vous permet d’ouvrir et de fermer les
dimensions, ou champs, d’un cube de décision, en appuyant sur des boutons.
• La grille de décision, TDecisionGrid, affiche des données unidimensionnelles ou
multidimensionnelles sous forme d’un tableau.
• Le graphe de décision, TDecisionGraph, affiche les champs en provenance
d’une grille de décision sous forme d’un graphe dynamique, qui change
lorsque les dimensions des données sont modifiées.

Utilisation de composants d’aide à la décision 27-1


Présentation des références croisées

La figure 27.1 montre tous les composants d’aide à la décision placés dans une
fiche au moment de la conception.
Figure 27.1 Composants d’aide à la décision au moment de la conception
Requête de décision
Cube de décision
Souce de décision

Pivot de décision

Grille de décision

Graphe de décision

Présentation des références croisées


Les références croisées sont une façon de présenter des sous-ensembles de
données afin de mettre en évidence des relations ou des tendances. Les champs
des tables deviennent les dimensions de la référence croisée tandis que les
valeurs des champs définissent les catégories et les calculs récapitulatifs dans
chaque dimension.
Vous pouvez utiliser les composants d’aide à la décision pour définir des
références croisées dans les fiches. TDecisionGrid représente les données dans un
tableau, tandis que TDecisionGraph les représente dans un graphe. TDecisionPivot
possède des boutons pour afficher ou cacher des dimensions et pour les
permuter entre lignes et colonnes.
Les références croisées peuvent avoir une ou plusieurs dimensions.

27-2 Guide du développeur


Instructions relatives à l’utilisation de composants d’aide à la décision

Références croisées à une dimension


Les références croisées à une dimension montrent une ligne (ou une colonne)
récapitulative des catégories d’une seule dimension. Par exemple, si Payment est
la dimension colonne choisie et si AmountPaid est le champ récapitulatif, la
référence croisée de la figure 27.2 montre le montant payé (AmountPaid) pour
chaque mode de règlement (Payment).
Figure 27.2 Référence croisée à une seule dimension

Références croisées à plusieurs dimensions


Les références croisées multidimensionnelles utilisent des dimensions
supplémentaires pour les lignes et/ou les colonnes. Par exemple, une référence
croisée à deux dimensions pourrait montrer le montant payé par mode de
règlement pour chaque pays.
Une référence croisée à trois dimensions pourrait montrer le montant payé
(AmountPaid) par mode de règlement (Payment) et par échéance (Terms) pour
chaque pays (Country), comme le montre la figure 27.3.
Figure 27.3 Référence croisée à trois dimensions

Instructions relatives à l’utilisation de composants d’aide à la


décision
Les composants d’aide à la décision dont la liste est à la page 27-1 peuvent être
utilisés ensemble pour présenter des données multidimensionnelles sous forme
de tableaux et de graphes. Plusieurs tableaux ou graphes peuvent être attachés à
chacun des ensembles de données. Plusieurs instances de TDecisionPivot peuvent
être utilisées pour afficher les données sous différents angles à l’exécution.

Utilisation de composants d’aide à la décision 27-3


Instructions relatives à l’utilisation de composants d’aide à la décision

Pour créer une fiche avec des tableaux et des graphes de données
multidimensionnelles, suivez ces étapes :
1 Créez une fiche.
2 Ajoutez ces composants à la fiche et utilisez l’inspecteur d’objets pour les lier
comme indiqué :
• un ensemble de données, habituellement TDecisionQuery (pour plus de
détails, reportez-vous à “Création d’ensembles de données de décision avec
l’éditeur de requête de décision” à la page 27-6) ou TQuery ;
• un cube de décision, TDecisionCube, lié à l’ensemble de données en
définissant la propriété DataSet du cube de décision par le nom de
l’ensemble de données ;
• une source de décision, TDecisionSource, liée au cube de décision en
définissant la propriété DecisionCube de la source de décision par le nom du
cube de décision.
3 Ajoutez un pivot de décision, TDecisionPivot, et liez-le à la source de décision
dans l’inspecteur d’objets en définissant la propriété DecisionSource du pivot
par le nom de la source de décision. Le pivot de décision est facultatif mais
utile ; il permet au développeur de fiches et à l’utilisateur final de changer les
dimensions affichées dans les grilles ou les graphes de décision en appuyant
sur des boutons.
Dans son orientation par défaut (horizontale), les boutons de gauche du pivot
de décision correspondent aux champs de gauche de la grille de décision (les
lignes) ; les boutons de droite correspondent aux champs du haut de la grille
de décision (les colonnes).
Vous pouvez déterminer l’endroit où apparaissent les boutons du pivot de
décision en définissant sa propriété GroupLayout par xtVertical, xtLeftTop ou
xtHorizontal (la valeur par défaut). Pour plus d’informations sur les propriétés
du pivot de décision, reportez-vous à “Utilisation de pivots de décision” à la
page 27-11.
4 Ajoutez un ou plusieurs graphes et/ou grilles de décision, liés à la source de
décision. Pour plus de détails, reportez-vous à “Création et utilisation de
grilles de décision” à la page 27-12 et “Création et utilisation de graphes de
décision” à la page 27-15.
5 Utilisez l’éditeur de requête de décision ou la propriété SQL de
TDecisionQuery (ou TQuery) pour spécifier les tables, les champs et les calculs
récapitulatifs à afficher dans la grille ou dans le graphe. Le dernier champ de
SQL SELECT peut être un champ récapitulatif. Les autres champs de SELECT
doivent être des champs GROUP BY. Pour en savoir plus, reportez-vous à
“Création d’ensembles de données de décision avec l’éditeur de requête de
décision” à la page 27-6.
6 Définissez la propriété Active de la requête de décision (ou d’un autre
composant ensemble de données) à True.

27-4 Guide du développeur


Utilisation d’ensembles de données avec les composants d’aide à la décision

7 Utilisez la grille et le graphe de décision pour montrer et représenter


graphiquement les différentes dimensions des données. Reportez-vous à
“Utilisation de grilles de décision” à la page 27-12 et “Utilisation de graphes
de décision” à la page 27-15 pour avoir des instructions et des suggestions.
Pour voir une illustration dans une fiche de tous les composants d’aide à la
décision, reportez-vous à la figure 27.1 à la page 27-2.

Utilisation d’ensembles de données avec les composants d’aide


à la décision
Le seul composant d’aide à la décision liant directement un ensemble de
données à un cube de décision est TDecisionCube. Le composant TDecisionCube
s’attend à recevoir des données dont les groupes et les calculs récapitulatifs ont
été définis par une instruction SQL d’un format acceptable. La phrase GROUP
BY doit contenir les mêmes champs non récapitulatifs (et dans le même ordre)
que la phrase SELECT. Les champs récapitulatifs doivent être identifiés.
Le composant requête de décision, TDecisionQuery, est une forme spécialisée de
TQuery. Vous pouvez utiliser TDecisionQuery pour définir de manière plus
simple les dimensions (lignes et colonnes) et les valeurs récapitulatives utilisées
pour fournir des données au cube de décision, TDecisionCube. Vous pouvez aussi
utiliser un TQuery ordinaire ou un autre ensemble de données, comme ensemble
de données pour TDecisionCube, mais la configuration correcte de l’ensemble de
données et de TDecisionCube est dès lors à la charge du concepteur.
Pour fonctionner correctement avec un cube de décision, tous les champs de
l’ensemble de données doivent être soit des dimensions, soit des champs
récapitulatifs. Les récapitulations doivent être de type additif (comme la somme
des valeurs ou le nombre de valeurs) et s’appliquent à chaque combinaison de
valeurs des dimensions. Pour faciliter la configuration, les noms des sommes de
l’ensemble de données peuvent commencer par “Sum...” tandis que ceux des
dénombrements peuvent commencer par “Count...”.
Le cube de décision ne peut pivoter, faire le sous-total ou forer que pour les
récapitulatifs dont les cellules sont additives (SUM et COUNT sont additives
alors que AVERAGE, MAX et MIN ne le sont pas). Ne concevez d’analyse
croisée que pour les grilles qui contiennent uniquement des agrégats additifs. Si
vous utilisez des agrégats non additifs, utilisez une grille de décision statique qui
n’effectue pas de pivot, de sous-total ou de forage.
Comme la moyenne peut être calculée en divisant SUM par COUNT, une
moyenne du pivot est ajoutée automatiquement quand les dimensions SUM et
COUNT d’un champ sont placées dans l’ensemble de données. Utilisez ce type
de moyenne de préférence à celle calculée par l’instruction AVERAGE.

Utilisation de composants d’aide à la décision 27-5


Utilisation d’ensembles de données avec les composants d’aide à la décision

Il est également possible de calculer des moyennes en utilisant COUNT(*). Pour


utiliser COUNT(*) afin de calculer des moyennes, placez un sélecteur "COUNT(*)
COUNTALL" dans la requête. Si vous utilisez COUNT(*) pour calculer des
moyennes, l’agrégat peut être utilisé pour tous les champs. N’utilisez COUNT(*)
que dans les cas où aucun des champs ne peut contenir de valeurs vierges ou si
l’opérateur COUNT n’est pas disponible pour tous les champs.

Création d’ensembles de données de décision avec TQuery ou


TTable
Si vous utilisez un composant TQuery ordinaire comme ensemble de données de
décision, vous devez configurer manuellement l’instruction SQL, en fournissant
une phrase GROUP BY qui contienne les mêmes champs (et dans le même
ordre) que la phrase SELECT.
L’instruction SQL doit ressembler à ce qui suit :
SELECT ORDERS."Terms", ORDERS."ShipVIA",
ORDERS."PaymentMethod", SUM( ORDERS."AmountPaid" )
FROM "ORDERS.DB" ORDERS
GROUP BY ORDERS."Terms", ORDERS."ShipVIA", ORDERS."PaymentMethod"
L’ordre des champs dans l’instruction SELECT doit correspondre à l’ordre des
champs de GROUP BY.
Avec TTable, vous devez spécifier au cube de décision les informations sur les
champs de la requête qui servent de regroupement et ceux servant de
récapitulatifs. Pour ce faire, remplissez la zone Type de dimension pour chaque
champ du DimensionMap du cube de décision. Il faut spécifier pour chaque
champ si c’est une dimension ou un récapitulatif et dans ce cas le type de
récapitulatif. Comme le calcul de moyenne de pivot dépend du calcul SUM/
COUNT, il faut également spécifier le nom de champ de base afin de permettre
au cube de décision d’associer les paires de récapitulatifs SUM et COUNT.

Création d’ensembles de données de décision avec l’éditeur de


requête de décision
Toutes les données utilisées par les composants d’aide à la décision passent par
le cube de décision, qui accepte un ensemble de données spécialement formaté,
le plus souvent produit par une requête SQL. Pour plus d’informations, voir
“Utilisation d’ensembles de données avec les composants d’aide à la décision” à
la page 27-5.
Bien que TTable et TQuery puissent être utilisés comme ensembles de données de
décision, il est plus facile d’utiliser TDecisionQuery; ; l’éditeur de requête de
décision, fourni avec lui, peut être utilisé pour spécifier les tables, les champs et
les calculs récapitulatifs qui apparaîtront dans le cube de décision, et vous aidera
à configurer correctement les parties SELECT et GROUP BY de l’instruction SQL.

27-6 Guide du développeur


Utilisation d’ensembles de données avec les composants d’aide à la décision

Utilisation de l’éditeur de requête de décision


Pour utiliser l’éditeur de requête de décision :
1 Sélectionnez le composant requête de décision dans la fiche, puis cliquez avec
le bouton droit de la souris et choisissez Editeur de requête de décision. La
boîte de dialogue Editeur de requête de décision apparaît.
2 Choisissez la base de données à utiliser.
3 Pour des requêtes sur une seule table, cliquez Sélection des tables et champs.
Pour les requêtes complexes mettant en oeuvre des jointures multitables,
cliquez sur le bouton Constructeur de requêtes pour afficher le constructeur
SQL ou tapez l’instruction SQL dans la boîte de saisie de la page d’onglet SQL.
4 Revenez à la boîte de dialogue Editeur de requête de décision.
5 Dans la boîte de dialogue Editeur de requête de décision, sélectionnez les
champs dans la boîte liste des champs disponibles et placez-les dans
Dimensions ou Récapitulatifs en cliquant sur le bouton flèche droite approprié.
A mesure que vous ajoutez des champs dans la liste Récapitulatifs,
sélectionnez dans le menu affiché le type de récapitulatif à utiliser : somme,
nombre ou moyenne.
6 Par défaut, tous les champs et calculs récapitulatifs définis dans la propriété
SQL de la requête de décision apparaissent dans les boîtes liste Dimensions et
Récapitulatifs. Pour supprimer une dimension ou un récapitulatif, sélectionnez
l’élément dans la liste et cliquez sur la flèche gauche située à côté de la liste,
ou double-cliquez sur l’élément à supprimer. Pour l’ajouter à nouveau,
sélectionnez-le dans la boîte liste des champs disponibles et cliquez sur la
flèche droite appropriée.
Lorsque le contenu de la requête de décision est défini, vous pouvez manipuler
ensuite l’affichage des dimensions avec la propriété DimensionMap et les boutons
de TDecisionPivot. Pour plus d’informations, voir la section suivante, “Utilisation
des cubes de décision,” “Utilisation de sources de décision” à la page 27-10, et
“Utilisation de pivots de décision” à la page 27-11.
Remarque Quand vous utilisez l’éditeur de requête de décision, la requête est initialement
gérée en utilisant la syntaxe SQL ANSI-92 puis, si c’est nécessaire, elle est ensuite
traduite dans le dialecte utilisé par le serveur. L’éditeur ne lit et n’affiche que du
SQL ANSI standard. La traduction dans le dialecte approprié est affectée
automatiquement à la propriété SQL du TDecisionQuery. Pour modifier une
requête, éditez la version ANSI-92 dans l’éditeur et pas celle contenue dans la
propriété SQL.

Propriétés d’une requête de décision


La requête de décision n’a pas d’autres propriétés que celles héritées des autres
composants. Les propriétés héritées importantes sont Active, décrite dans l’aide
en ligne et dans la référence de la bibliothèque des composants visuels à l’entrée
TDataSet, ainsi que SQL, décrite à l’entrée TQuery. Les requêtes sont décrites en
détail chapitre 21, “Manipulation des requêtes”.

Utilisation de composants d’aide à la décision 27-7


Utilisation des cubes de décision

Utilisation des cubes de décision


Le composant cube de décision, TDecisionCube, est un lieu de stockage de
données multidimensionnelles qui extrait ses données d’un ensemble de données
(généralement une instruction SQL spécialement structurée et entrée via
TDecisionQuery ou TQuery). Les données sont stockées d’une façon qui permet de
les réorganiser ou d’effectuer d’autres calculs récapitulatifs sans avoir besoin de
lancer la requête une seconde fois.

Propriétés et événements des cubes de décision


Les propriétés DimensionMap de TDecisionCube ne contrôlent pas seulement les
dimensions et les champs récapitulatifs qui apparaissent, mais permettent aussi
de définir des plages de données ou de spécifier le nombre maximal de
dimensions que le cube de décision pourra supporter. Vous pouvez aussi
indiquer d’afficher ou non les données au cours de la conception. Vous pouvez
afficher les noms, les valeurs (catégories), les sous-totaux ou les données.
L’affichage des données pendant la conception peut prendre du temps, selon la
source des données.
Lorsque vous cliquez sur les points de suspension en regard de DimensionMap,
dans l’inspecteur d’objets, la boîte de dialogue Editeur de cube de décision
apparaît. Vous pouvez utiliser ses pages et ses contrôles pour définir les
propriétés DimensionMap.
L’événement OnRefresh est déclenché chaque fois qu’est reconstruit le cache du
cube de décision. Les développeurs peuvent accéder aux nouvelles valeurs des
propriétés DimensionMap et les changer à ce moment-là pour libérer la mémoire,
changer le nombre maximal de dimensions ou de champs récapitulatifs, etc.
OnRefresh sert également lorsque les utilisateurs accèdent à l’éditeur de cube de
décision ; le code de l’application peut alors répondre aux modifications
apportées par l’utilisateur.

Utilisation de l’éditeur de cube de décision


Vous pouvez utiliser l’éditeur de cube de décision pour définir les propriétés
DimensionMap des cubes de décision. Vous pouvez afficher l’éditeur de cube de
décision via l’inspecteur d’objets, comme indiqué dans la section précédente.
Vous pouvez aussi cliquer avec le bouton droit sur un cube de décision dans
une fiche au moment de la conception et choisir Editeur de cube de décision.
La boîte de dialogue Editeur de cube de décision possède deux onglets :
• Paramètres de dimensions, utilisé pour activer ou désactiver les dimensions
disponibles, renommer et reformater des dimensions, placer des dimensions
dans un état “perforé de manière permanente”, et définir les plages de valeurs
à afficher.

27-8 Guide du développeur


Utilisation des cubes de décision

• Contrôle de la mémoire, utilisé pour définir le nombre maximal de dimensions


et de champs récapitulatifs pouvant être actifs en même temps, pour afficher
des informations sur l’utilisation de la mémoire et pour déterminer les noms
et les données qui apparaissent à la conception.

Visualisation et modification des paramètres de dimensions


Pour visualiser les paramètres de dimensions, affichez l’éditeur de cube de
décision et cliquez sur l’onglet Paramètres de dimensions. Ensuite, sélectionnez
une dimension ou un champ récapitulatif dans la liste des champs disponibles.
Ces informations apparaissent dans les boîtes situées sur le côté droit de
l’éditeur :
• Pour modifier le nom d’une dimension ou d’un champ récapitulatif
apparaissant sur le pivot, la grille ou le graphe de décision, entrez un
nouveau nom dans la boîte de saisie Nom affichée.
• Pour savoir si le champ sélectionné est une dimension ou un champ
récapitulatif, lisez le texte se trouvant dans la boîte de saisie Type. Si
l’ensemble de données est un composant TTable, vous pouvez utiliser Type
pour spécifier si le champ sélectionné est une dimension ou un champ
récapitulatif.
• Pour désactiver ou activer la dimension ou le champ récapitulatif sélectionné,
modifiez le paramétrage de la boîte liste déroulante Type actif : Actif, Si
besoin ou Inactif. Désactiver une dimension ou la définir par Si besoin
économise de la mémoire.
• Pour modifier le format de cette dimension ou de ce champ récapitulatif,
entrez une chaîne de formatage dans la boîte de saisie Format.
• Pour afficher cette dimension ou ce champ récapitulatif par Année, Trimestre
ou Mois, changez le paramétrage de la boîte liste déroulante Groupage. Dans
la boîte liste Groupage, vous pouvez placer la dimension ou le récapitulatif
sélectionné dans un état "perforé" permanent. Cela peut être utile pour
économiser de la mémoire lorsqu’une dimension a de nombreuses valeurs.
Pour plus d’informations, voir “Considérations relatives au contrôle de la
mémoire” à la page 27-21.
• Pour déterminer le point de départ des intervalles, commencez par choisir la
valeur adaptée de regroupement dans la liste déroulante Groupage puis entrez
la valeur de départ de l’intervalle dans la liste déroulante Valeur initiale.

Définition du maximum de dimensions et de récapitulations


Pour déterminer le nombre maximal de dimensions et de champs récapitulatifs
disponibles pour les pivots, les grilles et les graphes liés au cube de décision
sélectionné, affichez l’éditeur de cube de décision, cliquez sur l’onglet Contrôle
de la mémoire et utilisez les contrôles de saisie pour ajuster le paramétrage en
cours, si nécessaire. Ces paramètres permettent de contrôler la quantité de
mémoire nécessaire au cube de décision. Pour plus d’informations, reportez-vous
à “Considérations relatives au contrôle de la mémoire” à la page 27-21.

Utilisation de composants d’aide à la décision 27-9


Utilisation de sources de décision

Visualisation et modification des options de conception


Pour déterminer combien d’informations apparaîtront lors de la conception,
affichez l’éditeur de cube de décision, cliquez sur l’onglet Contrôle de la
mémoire. Cochez ensuite les paramètres qui indiquent les noms et les données à
afficher. L’affichage des données ou des noms des champs lors de la conception
peut diminuer les performances dans certains cas à cause du temps nécessaire à
l’extraction des données.

Utilisation de sources de décision


Le composant source de décision, TDecisionSource, définit l’état en cours des
pivots des grilles ou des graphes de décision. Lorsque deux de ces objets
utilisent la même source de décision, leurs pivots partagent le même état.

Propriétés et événements
Voici les propriétés et les événements spéciaux qui contrôlent l’aspect et le
comportement des sources de décision :
• La propriété ControlType de TDecisionSource indique si les boutons du pivot de
décision doivent agir comme des cases à cocher (sélections multiples) ou des
boutons radio (sélections mutuellement exclusives).
• Les propriétés SparseCols et SparseRows de TDecisionSource indiquent si les
colonnes ou les lignes sans valeur doivent être affichées ; si True, les colonnes
ou les lignes vides sont affichées.
• TDecisionSource possède les événements suivants :
• OnLayoutChange se produit lorsque l’utilisateur effectue des pivotements ou
des perforations qui réorganisent les données.
• OnNewDimensions se produit lorsque les données elles-mêmes sont
modifiées, par exemple lorsque les champs récapitulatifs ou les dimensions
sont modifiés.
• OnSummaryChange se produit lorsque la valeur récapitulative en cours est
modifiée.
• OnStateChange se produit quand le cube de décision est activé ou désactivé.
• OnBeforePivot se produit lorsque les modifications sont validées mais pas
encore reflétées par l’interface utilisateur. Les développeurs ont la
possibilité d’effectuer les changements, par exemple de capacité ou de l’état
du pivot, avant que l’utilisateur de l’application ne puisse voir le résultat
de son action.
• OnAfterPivot est déclenché après une modification de l’état du pivot. Les
développeurs peuvent intercepter des informations à ce moment.

27-10 Guide du développeur


Utilisation de pivots de décision

Utilisation de pivots de décision


Le composant pivot de décision, TDecisionPivot, vous permet d’ouvrir ou de
fermer les dimensions, ou champs, d’un cube de décision en appuyant sur des
boutons. Lorsqu’une ligne ou une colonne est ouverte en appuyant sur un
bouton TDecisionPivot, la dimension correspondante apparaît dans le composant
TDecisionGrid ou TDecisionGraph. Lorsqu’une dimension est fermée, le détail de
ses données n’apparaît pas ; elles s’intègrent aux totaux des autres dimensions.
Une dimension peut aussi être en état “perforé”, état dans lequel seules les
valeurs récapitulatives pour une catégorie particulière de la dimension
apparaissent.
Vous pouvez utiliser le pivot de décision pour réorganiser les dimensions
affichées par la grille et le graphe de décision. Faites simplement glisser un
bouton vers la partie des lignes ou celle des colonnes, ou réorganisez les boutons
dans la même partie.
Pour voir des illustrations de pivots de décision pendant la conception, reportez-
vous aux figures 27.1, 27.2 et 27.3.

Propriétés des pivots de décision


Voici les propriétés spéciales qui contrôlent l’aspect et le comportement des
pivots de décision :
• Les premières propriétés de TDecisionPivot définissent leur aspect et leur
comportement généraux. Vous pouvez définir la propriété ButtonAutoSize de
TDecisionPivot par False pour empêcher la réduction et le développement des
boutons lorsque vous ajustez la taille du composant.
• La propriété Groups de TDecisionPivot définit quels boutons de dimensions
apparaîtront. Vous pouvez grouper les boutons de sélection des lignes, des
colonnes et des champs récapitulatifs à votre gré. Si vous voulez une
disposition des groupes plus simple, vous pouvez placer quelque part sur
votre fiche un TDecisionPivot contenant uniquement les lignes, et ailleurs un
second contenant uniquement les colonnes.
• Généralement, TDecisionPivot est ajouté au-dessus de TDecisionGrid. Dans cette
orientation par défaut (horizontale), les boutons du côté gauche de
TDecisionPivot s’appliquent aux champs du côté gauche de TDecisionGrid
(lignes) ; les boutons du côté droit s’appliquent aux champs du haut de
TDecisionGrid (colonnes).
• Vous pouvez déterminer où apparaîtront les boutons de TDecisionPivot en
définissant sa propriété GroupLayout par xtVertical, xtLeftTop ou xtHorizontal
(valeur par défaut décrite au paragraphe précédent).

Utilisation de composants d’aide à la décision 27-11


Création et utilisation de grilles de décision

Création et utilisation de grilles de décision


Les composants grille de décision, TDecisionGrid, présentent des références
croisées sous forme de tableaux. Ces tableaux de références croisées, également
appelés tableaux croisés, sont décrits à la page 27-2. La figure 27.1 à la page 27-2
montre une grille de décision placée sur une fiche pendant la conception.

Création de grilles de décision


Pour créer une fiche contenant un ou plusieurs tableaux de références croisées,
1 Suivez les étapes 1 à 3 de la section “Instructions relatives à l’utilisation de
composants d’aide à la décision” à la page 27-3.
2 Ajoutez un ou plusieurs composants grille de décision (TDecisionGrid) et
définissez dans l’inspecteur d’objets leur propriété DecisionSource par le nom
du composant source de décision, TDecisionSource, auquel vous voulez relier
les grilles.
3 Continuez par les étapes 5 à 7 de la section “Instructions relatives à
l’utilisation de composants d’aide à la décision.”
Pour avoir la description de ce qui apparaît dans la grille de décision et savoir
comment l’utiliser, lisez “Utilisation de grilles de décision” à la page 27-12.
Pour ajouter un graphe à la fiche, suivez les instructions de “Création de
graphes de décision” à la page 27-15.

Utilisation de grilles de décision


Le composant grille de décision, TDecisionGrid, affiche les données du cube de
décision (TDecisionCube) lié à la source de décision (TDecisionSource).
Par défaut, la grille apparaît avec les champs dimension à gauche ou en haut
selon le groupage défini dans l’ensemble de données. Les catégories, une pour
chaque valeur, apparaissent sous chacun des champs. Vous pouvez :
• Ouvrir et fermer les dimensions
• Réorganiser, ou faire pivoter, les lignes et les colonnes
• “Forer” pour obtenir les détails
• Limiter la sélection des dimensions à une seule dimension par axe
Pour plus d’informations sur les propriétés et les événements relatifs à la grille
de décision, voir “Propriétés des grilles de décision” à la page 27-14.

Ouverture et fermeture des champs d’une grille de décision


Un signe plus (+) apparaît dans un champ dimension ou récapitulatif, quand un
ou plusieurs champs sont fermés (cachés) à sa droite. Vous pouvez ouvrir
d’autres champs et d’autres catégories en cliquant sur le signe plus. Un signe

27-12 Guide du développeur


Création et utilisation de grilles de décision

moins (-) indique un champ complètement ouvert (développé). Lorsque vous


cliquez sur le signe moins, le champ se ferme. Cette possibilité de
développement peut être désactivée ; pour plus de détails, voir “Propriétés des
grilles de décision” à la page 27-14.

Réorganisation des lignes et des colonnes d’une grille de décision


Vous pouvez faire glisser des titres de lignes et de colonnes le long du même
axe ou vers d’autres axes. Vous pouvez ainsi réorganiser la grille et examiner les
données sous un angle nouveau, au fur et à mesure que vous changez le
regroupement des données. La possibilité de pivoter peut être désactivée ; pour
plus de détails, voir “Propriétés des grilles de décision” à la page 27-14.
Si vous incluez un pivot de décision, vous pouvez réorganiser l’affichage en
appuyant sur ses boutons ou en les faisant glisser. Voir les instructions de
“Utilisation de pivots de décision” à la page 27-11.

Perforation pour voir les détails dans les grilles de décision


Vous pouvez “forer” pour voir une dimension en détail.
Par exemple, si vous cliquez avec le bouton droit sur un libellé de catégorie (titre
de ligne) pour une dimension qui en contient d’autres, vous pouvez choisir de
“forer” et de voir uniquement les données de cette catégorie. Lorsqu’une
dimension est “perforée”, les libellés des catégories de cette dimension ne
s’affichent pas sur la grille, car seuls les enregistrements correspondant à une
seule catégorie sont affichés. Si vous avez un pivot de décision sur la fiche, il
affiche les valeurs des autres catégories et vous permet d’en changer.
Pour “forer” dans une dimension,
• Cliquez avec le bouton droit de la souris sur le libellé d’une catégorie et
choisissez Percer jusqu’à cette valeur, ou
• Cliquez avec le bouton droit de la souris sur un bouton du pivot et choisissez
Perforé.
Pour que la dimension complète soit de nouveau active,
• Cliquez avec le bouton droit de la souris sur le bouton correspondant du
pivot ou bien, cliquez avec le bouton droit de la souris dans le coin supérieur
gauche de la grille de décision et sélectionnez la dimension.

Limite des dimensions à sélectionner dans les grilles de décision


Vous pouvez changer la propriété ControlType de la source de décision pour
déterminer si plusieurs dimensions peuvent être sélectionnées pour chaque axe
de la grille. Pour plus d’informations, voir “Utilisation de sources de décision” à
la page 27-10.

Utilisation de composants d’aide à la décision 27-13


Création et utilisation de grilles de décision

Propriétés des grilles de décision


Le composant grille de décision, TDecisionGrid, affiche les données du composant
TDecisionCube lié à TDecisionSource. Par défaut, les données apparaissent dans
une grille avec les champs de catégorie à gauche et en haut de la grille.
Voici quelques propriétés spéciales qui contrôlent l’aspect et le comportement
des grilles de décision :
• TDecisionGrid a des propriétés uniques pour chaque dimension. Pour les
définir, choisissez Dimensions dans l’inspecteur d’objets, puis sélectionnez une
dimension. Ses propriétés apparaissent alors dans l’inspecteur d’objets :
Alignment définit l’alignement des libellés des catégories de cette dimension,
Caption peut remplacer le nom par défaut de la dimension, Color définit la
couleur des libellés des catégories, FieldName affiche le nom de la dimension
active, Format peut contenir tout format standard pour ce type de données et
Subtotals indique s’il faut afficher les sous-totaux pour cette dimension. Ces
mêmes propriétés sont utilisées avec les champs récapitulatifs pour changer
l’aspect des données récapitulatives de la grille. Pour définir les propriétés des
dimensions, cliquez sur un composant dans la fiche ou choisissez le
composant dans la liste déroulante située en haut de l’inspecteur d’objets.
• La propriété Options de TDecisionGrid vous permet de contrôler l’affichage des
lignes de la grille (cgGridLines = True), d’activer la fonction de réduction et de
développement des dimensions avec les indicateurs + et - (cgOutliner = True)
et d’activer la possibilité de pivoter par glisser-déplacer (cgPivotable = True).
• L’événement OnDecisionDrawCell de TDecisionGrid vous permet de changer
l’aspect de chaque cellule au moment où elle est dessinée. L’événement passe
en tant que paramètres par référence les valeurs de String, Font et Color de la
cellule en cours. Vous êtes libre de modifier ces paramètres pour réaliser des
effets, par exemple choisir une couleur particulière pour les valeurs négatives.
En plus de la propriété Drawstate qui est passée par TCustomGrid, l’événement
transmet la valeur de TDecisionDrawState, qui peut être utilisée pour
déterminer le type de cellule à dessiner. D’autres informations concernant la
cellule peuvent être extraites via les fonctions Cells, CellValueArray ou
CellDrawState.
• L’événement OnDecisionExamineCell de TDecisionGrid vous permet de connecter
l’événement clic-droit aux cellules de données, afin de pouvoir afficher des
informations (par exemple, des enregistrements détail) sur une cellule
particulière. Lorsque l’utilisateur clique avec le bouton droit de la souris sur
une cellule, l’événement est fourni avec toutes les informations qui entrent en
jeu, c’est-à-dire la valeur récapitulative en cours et un tableau ValueArray
contenant toutes les valeurs de la dimension utilisées pour calculer la valeur
récapitulative.

27-14 Guide du développeur


Création et utilisation de graphes de décision

Création et utilisation de graphes de décision


Les composants graphe de décision, TDecisionGraph, présentent des références
croisées sous forme de graphes. Chaque graphe de décision montre la valeur
d’un seul calcul récapitulatif, la somme, le nombre ou la moyenne, pour une ou
plusieurs dimensions. Pour plus d’informations sur les références croisées, voir
page 27-3. Pour voir des illustrations sur les graphes de décision pendant la
conception, voir les figures 27.1 à la page 27-2 et 27.4 à la page 27-16.

Création de graphes de décision


Pour créer une fiche ayant un ou plusieurs graphes de décision,
1 Suivez les étapes 1 à 3 de la section “Instructions relatives à l’utilisation de
composants d’aide à la décision” à la page 27-3.
2 Ajoutez un ou plusieurs composants graphe de décision (TDecisionGraph) et
définissez dans l’inspecteur d’objets leur propriété DecisionSource par le nom
du composant source de décision , TDecisionSource, auquel vous voulez relier
les graphes.
3 Continuez avec les étapes 5 à 7 de la section “Instructions relatives à
l’utilisation de composants d’aide à la décision.”
4 Enfin, cliquez avec le bouton droit de la souris sur le graphe et choisissez
Modifier le graphe pour changer l’aspect des séries du graphe. Vous pouvez
définir des propriétés modèles pour chaque dimension du graphe, puis définir
les propriétés de chaque série pour remplacer ces valeurs par défaut. Pour
plus de détails, voir “Personnalisation du graphe de décision” à la page 27-17.
Pour avoir la description de ce qui apparaît dans le graphe de décision et savoir
comment l’utiliser, lisez la section suivante, “Utilisation de graphes de décision.”
Pour ajouter une grille de décision (ou tableau croisé) à la fiche, suivez les
instructions de “Création et utilisation de grilles de décision” à la page 27-12.

Utilisation de graphes de décision


Le composant graphe de décision, TDecisionGraph, affiche les champs de la
source de décision (TDecisionSource) sous forme d’un graphe dynamique qui
change lorsque les dimensions de données sont ouvertes, fermées, déplacées ou
réorganisées à l’aide du pivot de décision (TDecisionPivot).
Les données représentées viennent d’un ensemble de données spécialement
formaté, tel que TDecisionQuery. Pour avoir un aperçu de la façon dont les
composants d’aide à la décision gèrent et disposent ces données, voir page 27-1.
Par défaut, la dimension de la première ligne est représentée par l’axe des x et la
dimension de la première colonne par l’axe des y.

Utilisation de composants d’aide à la décision 27-15


Création et utilisation de graphes de décision

Vous pouvez utiliser les graphes de décision à la place, ou en plus, des grilles de
décision (qui elles, présentent les références croisées sous forme de tableaux). Les
grilles ou les graphes de décision qui sont liés à la même source de décision
représentent les mêmes dimensions de données. Pour montrer différentes
données récapitulatives pour les mêmes dimensions, vous pouvez lier plusieurs
graphes de décision à la même source de décision. Pour montrer différentes
dimensions, liez les graphes de décision à différentes sources de décision.
Par exemple, dans la figure 27.4, le premier pivot de décision et le premier
graphe sont liés à la première source de décision alors que le second pivot de
décision et le second graphe sont liés à la seconde source de décision. Chaque
graphe peut donc représenter des dimensions différentes.
Figure 27.4 Graphes de décision liés à différentes sources de décision

Pour plus d’informations sur ce qui apparaît dans un graphe de décision, voir la
section suivante, “Affichage du graphe de décision.”
Pour créer un graphe de décision, voir la section précédente, “Création de
graphes de décision.”
Pour connaître les propriétés des graphes de décision et savoir comment changer
l’aspect et le comportement des graphes de décision, voir “Personnalisation du
graphe de décision” à la page 27-17.

27-16 Guide du développeur


Création et utilisation de graphes de décision

Affichage du graphe de décision


Par défaut, le graphe de décision représente les valeurs récapitulatives des
catégories existant dans le premier champ de la ligne active (le long de l’axe
des y) par rapport aux valeurs du premier champ de la colonne active (le long
de l’axe des x). Chaque catégorie est représentée par une série.
Si une seule dimension est sélectionnée (par exemple, en cliquant sur un seul
bouton de TDecisionPivot), une seule série est représentée.
Si vous utilisez un pivot de décision, vous pouvez appuyer sur ses boutons pour
déterminer les champs (dimensions) du cube de décision qui doivent être
représentés. Pour échanger les axes du graphe, faites glisser les boutons de
dimension du pivot de décision de part et d’autre de l’espace séparateur. Si le
graphe est unidimensionnel, avec tous les boutons d’un côté de l’espace
séparateur, vous pouvez utiliser les icônes de lignes ou de colonnes comme cible
du déplacement pour ajouter des boutons de l’autre côté du séparateur et rendre
le graphe multidimensionnel.
Si vous voulez qu’une seule ligne ou qu’une seule colonne soit active à la fois,
vous pouvez donner la valeur xtRadio à la propriété ControlType de
TDecisionSource. Un seul champ pourra alors être actif à la fois, et la
fonctionnalité du pivot de décision correspondra au comportement du graphe.
xtRadioEx fonctionne comme xtRadio, mais n’autorise pas l’état où les dimensions
de toutes les lignes ou de toutes les colonnes sont fermées.
Si vous avez à la fois une grille et un graphe de décision connectés à la même
TDecisionSource, il vaudra mieux définir ControlType par xtCheck pour revenir au
comportement le plus souple de TDecisionGrid.

Personnalisation du graphe de décision


Le composant graphe de décision, TDecisionGraph, affiche les champs de la
source de décision (TDecisionSource) sous forme d’un graphe dynamique qui
change quand les dimensions sont ouvertes, fermées, déplacées ou réorganisées à
l’aide du pivot de décision (TDecisionPivot). Vous pouvez modifier le type, les
couleurs, les types de marqueurs des graphes linéaires et de nombreuses autres
propriétés des graphes de décision.
Pour personnaliser un graphe,
1 Cliquez dessus avec le bouton droit de la souris et choisissez Modifier le
graphe. La boîte de dialogue de modification de graphe apparaît.
2 Utilisez la page Graphe de la boîte de dialogue de modification de graphe
pour voir la liste des séries visibles, sélectionner la définition de série à utiliser
si deux ou plus sont disponibles pour la même série, changer le type de
graphe d’un modèle ou d’une série et définir les propriétés globales du
graphe.

Utilisation de composants d’aide à la décision 27-17


Création et utilisation de graphes de décision

La liste Séries de la page Graphe montre toutes les dimensions du cube de


décision (précédées de Modèle:) et les catégories actuellement visibles. Chaque
catégorie, ou série, est un objet séparé. Vous pouvez :
• ajouter ou supprimer des séries dérivées des séries existantes du graphe.
Les séries dérivées peuvent fournir des annotations pour des séries
existantes ou représenter des valeurs calculées à partir d’autres séries ;
• changer le type de graphe par défaut et changer le titre des modèles et des
séries.
Vous trouverez dans l’aide en ligne la description des autres onglets de la
page Graphe.
3 Utilisez la page Séries pour établir les modèles de dimensions, puis
personnaliser les propriétés de chaque série du graphe.
Par défaut, les séries sont représentées par des barres d’histogramme qui
peuvent avoir jusqu’à 16 couleurs. Vous pouvez modifier le type et les propriétés
du modèle pour créer un nouveau modèle par défaut. Lorsque vous utilisez le
pivot pour faire passer la source de décision par différents états, le modèle est
utilisé pour créer de façon dynamique la série de chaque nouvel état. Pour avoir
plus de détails sur les modèles, voir “Définition des modèles de graphe de
décision par défaut” à la page 27-18.
Pour personnaliser une série individuelle, suivez les instructions de
“Personnalisation des séries d’un graphe de décision” à la page 27-19.
Vous trouverez dans l’aide en ligne la description des autres onglets de la page
Séries.

Définition des modèles de graphe de décision par défaut


Les graphes de décision affichent les valeurs provenant de deux dimensions du
cube de décision : l’une est représentée par un axe et l’autre est utilisée pour
créer l’ensemble des séries. Le modèle de cette dimension fournit les valeurs par
défaut des propriétés des séries (si la série est représentée par une barre, une
ligne, une aire, etc.) Au fur et à mesure que les utilisateurs pivotent d’un état
vers l’autre, les séries exigées pour la dimension sont créées en utilisant le type
de série et les autres valeurs par défaut spécifiées dans le modèle.
Un modèle distinct est fourni pour le cas où les utilisateurs pivotent vers un état
dans lequel une seule dimension est active. Un état unidimensionnel est souvent
représenté par un graphique sectoriel, et un modèle est fourni pour ce cas.
Vous pouvez :
• Changer le type du graphe par défaut.
• Changer les autres propriétés du modèle de graphe.
• Voir et définir les propriétés générales du graphe.

27-18 Guide du développeur


Création et utilisation de graphes de décision

Changement du type de graphe de décision par défaut


Pour changer le type du graphe par défaut,
1 Sélectionnez un modèle dans la liste Séries de la page Graphe de la boîte de
dialogue de modification de graphe.
2 Cliquez sur le bouton Modifier.
3 Sélectionnez un nouveau type et fermez la boîte de dialogue Galerie.

Changement des autres propriétés d’un modèle de graphe de décision


Pour changer la couleur ou les autres propriétés d’un modèle,
1 Sélectionnez la page Séries, en haut de la boîte de dialogue de modification de
graphe.
2 Choisissez un modèle dans la liste déroulante en haut de la page.
3 Choisissez l’onglet correspondant à la propriété à modifier et faites vos choix.

Visualisation des propriétés globales d’un graphe de décision


Pour voir et définir les propriétés d’un graphe de décision autres que le type ou
les séries,
1 Sélectionnez la page Graphe en haut de la boîte de dialogue de modification
de graphe.
2 Choisissez l’onglet correspondant à la propriété à modifier et faites vos choix.

Personnalisation des séries d’un graphe de décision


Les modèles fournissent de nombreux paramètres par défaut pour chaque
dimension du cube de décision, tels le type de graphe et la façon d’afficher les
séries. D’autres paramètres par défaut, tels les couleurs des séries, sont définis par
TDecisionGraph. Vous pouvez remplacer les paramètres par défaut de chaque série.
Les modèles doivent être utilisés pour que le programme crée les séries
correspondant aux catégories et doivent être abandonnés quand ce n’est plus
nécessaire. Si vous voulez, vous pouvez définir des séries personnalisées pour
des valeurs particulières de catégories. Utilisez le pivot afin que le graphe affiche
une série pour la catégorie que vous voulez personnaliser. Quand la série est
affichée sur le graphe, vous pouvez utiliser l’éditeur de graphe pour :
• Changer le type de graphe.
• Changer d’autres propriétés concernant les séries.
• Enregistrer les séries spécifiques au graphe que vous venez de personnaliser.
Pour définir des modèles de séries et définir des options par défaut globales,
voir “Définition des modèles de graphe de décision par défaut” à la page 27-18.

Changement du type de graphe des séries


Par défaut, les séries ont toutes le même type de graphe, défini par le modèle de
sa dimension. Pour changer toutes les séries d’un même graphe, il suffit de
changer le type du modèle. Pour ce faire, reportez-vous à “Changement du type
de graphe de décision par défaut” à la page 27-19.

Utilisation de composants d’aide à la décision 27-19


Utilisation des composants d’aide à la décision à l’exécution

Pour changer le type de graphe d’une seule série,


1 Sélectionnez une série dans la liste Séries de la page Graphe de l’éditeur de
graphe.
2 Cliquez sur le bouton Modifier.
3 Sélectionnez un nouveau type et fermez la boîte de dialogue Galerie.
4 Activez la case à cocher d’enregistrement des séries.

Changement des autres propriétés des séries d’un graphe de décision


Pour changer la couleur ou d’autres propriétés des séries d’un graphe de décision,
1 Sélectionnez la page Séries, en haut de la boîte de dialogue de modification de
graphe.
2 Choisissez une série dans la liste déroulante, en haut de la page.
3 Choisissez l’onglet correspondant à la propriété à modifier et faites vos choix.
4 Activez la case à cocher d’enregistrement des séries.

Enregistrement des paramètres des séries d’un graphe de décision


Par défaut, seuls les paramètres des modèles sont enregistrés en mode
conception. Les modifications faites à des séries particulières ne sont enregistrées
que si la case d’enregistrement de ces séries est cochée dans la boîte de dialogue
de modification de graphe.
L’enregistrement des séries occupant beaucoup de mémoire, pensez à désactiver
cette case quand vous n’avez pas besoin de les enregistrer.

Utilisation des composants d’aide à la décision à l’exécution


A l’exécution, les utilisateurs peuvent effectuer de nombreuses opérations en
cliquant avec le bouton gauche, en cliquant avec le bouton droit et en faisant
glisser les composants d’aide à la décision visibles. Ces opérations, décrites plus
haut, sont résumées ici.

Pivots de décision à l’exécution


Les utilisateurs peuvent :
• cliquer avec le bouton gauche sur le bouton récapitulatif, à l’extrémité gauche
du pivot de décision, pour afficher la liste des récapitulations disponibles. Ils
peuvent utiliser cette liste pour changer les données récapitulatives affichées
dans les grilles et les graphes de décision.
• Cliquer avec le bouton droit de la souris sur un bouton de dimension et
choisir de :
• le déplacer de la partie lignes vers la partie colonnes ou l’inverse ;
• “forer” pour afficher les données détail.

27-20 Guide du développeur


Considérations relatives au contrôle de la mémoire

• Cliquer avec le bouton gauche sur un bouton de dimension, après avoir choisi
la commande de forage et sélectionner :
• ouvrir dimension, pour revenir au niveau supérieur de cette dimension ;
• toutes les valeurs, pour basculer entre l’affichage dans les grilles de
décision des récapitulations seulement ou des récapitulations plus les autres
valeurs.
• à partir de la liste des catégories disponibles, une catégorie à “forer” pour
connaître les valeurs détail.
• Cliquer avec le bouton gauche sur un bouton de dimension pour ouvrir ou
fermer cette dimension.
• Faire glisser les boutons de dimension depuis la partie lignes vers la partie
colonnes et réciproquement ; ils peuvent ensuite les placer à côté des boutons
existant dans cette partie ou sur l’icône de lignes ou de colonnes.

Grilles de décision à l’exécution


Les utilisateurs peuvent :
• Cliquer avec le bouton droit de la souris à l’intérieur de la grille de décision
et choisir l’une des possibilités suivantes :
• activer et désactiver alternativement les sous-totaux pour des groupes
individuels de données, pour toutes les valeurs d’une dimension, ou pour
toute la grille ;.
• afficher l’éditeur de cube de décision, décrit à la page 27-8.
• ouvrir et fermer alternativement les dimensions et les récapitulations.
• Cliquer sur + et sur – dans les titres de lignes ou de colonnes pour ouvrir et
fermer les dimensions.
• Faire glisser les dimensions des lignes vers les colonnes et réciproquement.

Graphes de décision à l’exécution


Les utilisateurs peuvent faire glisser la souris d’un côté à l’autre ou de haut en
bas du graphe pour faire défiler les catégories et les valeurs non visibles à l’écran.

Considérations relatives au contrôle de la mémoire


Un champ dimension ou récapitulatif chargé dans le cube de décision occupe de
l’espace mémoire. L’ajout d’un nouveau champ récapitulatif augmente de façon
linéaire l’occupation mémoire : par exemple, un cube de décision avec deux
champs récapitulatifs occupe deux fois plus de mémoire qu’avec un seul, avec
trois champs récapitulatifs il occupe trois fois plus de mémoire, etc. La

Utilisation de composants d’aide à la décision 27-21


Considérations relatives au contrôle de la mémoire

consommation de mémoire pour les dimensions augmente plus vite. L’ajout


d’une dimension de 10 valeurs multiplie par 10 la consommation de mémoire
(par rapport à l’ajout d’une dimension qui aurait une seule valeur) et l’ajout
d’une dimension de 100 valeurs la multiplie par 100. L’ajout de dimensions à un
cube de décision peut avoir un effet dramatique sur l’utilisation de la mémoire
et entraîner très vite une baisse des performances. Cet effet est particulièrement
prononcé quand les dimensions ajoutées comportent de nombreuses valeurs.
Les composants d’aide à la décision contiennent un certain nombre d’options qui
vous aident à contrôler comment et quand la mémoire est utilisée. Pour plus
d’informations sur les propriétés et les techniques indiquées ici, recherchez
TDecisionCube dans l’aide en ligne.

Définition du maximum de dimensions, de champs récapitulatifs,


et de cellules
Les propriétés MaxDimensions et MaxSummaries des cubes de décision sont
utilisées avec la propriété CubeDim.ActiveFlag pour contrôler le nombre de
dimensions et de champs récapitulatifs pouvant être chargés en même temps.
Dans l’éditeur de cube de décision (groupe Capacité du cube, page Contrôle de
la mémoire), vous pouvez définir le nombre maximal de valeurs afin de
contrôler le nombre de dimensions et de champs récapitulatifs pouvant être
présents en mémoire.
La limitation du nombre de dimensions et de champs récapitulatifs permet de
réduire grossièrement la quantité de mémoire utilisée par le cube de décision.
Mais, elle ne permet pas de distinguer les dimensions ayant peu de valeurs de
celles en ayant beaucoup. Pour avoir un meilleur contrôle des besoins en
mémoire du cube de décision, vous devez aussi limiter le nombre de cellules.
Définissez le nombre maximal de cellules dans l’éditeur de cube de décision
(groupe Capacité du cube, page Contrôle de la mémoire).

Définition de l’état des dimensions


La propriété ActiveFlag contrôle les dimensions à charger. Vous pouvez définir
cette propriété dans la page Paramètres de dimensions de l’éditeur de cube de
décision, en utilisant Type actif. Quand ce contrôle est mis à Actif, la dimension
sera chargée inconditionnellement et occupera toujours l’espace mémoire. Notez
que le nombre de dimensions dans cet état doit toujours être inférieur à
MaxDimensions, et que le nombre des champs récapitulatifs mis à Actif doit être
inférieur à MaxSummaries. Ne mettez à Actif une dimension ou un champ
récapitulatif que s’il faut absolument qu’il soit disponible à tout moment. Le
choix de Actif diminue la capacité de mémoire disponible que peut gérer le cube.
Lorsque ActiveFlag est définie à AsNeeded, une dimension ou un champ
récapitulatif n’est chargé que s’il peut l’être sans dépasser les limites de
MaxDimensions, MaxSummaries ou MaxCells. Le cube de décision permutera en et
hors mémoire les dimensions et les champs récapitulatifs marqués AsNeeded pour
respecter les limites imposées par MaxCells, MaxDimensions et MaxSummaries.

27-22 Guide du développeur


Considérations relatives au contrôle de la mémoire

C’est-à-dire qu’une dimension ou un champ récapitulatif ne sera pas en mémoire


quand il n’est pas utilisé. Définir par AsNeeded les dimensions qui ne sont pas
fréquemment utilisées entraîne de meilleures performances pour le chargement et
le pivotement, malgré le temps d’accès aux dimensions non chargées.

Utilisation de dimensions paginées


Quand Binning a la valeur Set dans la page Paramètres de dimensions de
l’éditeur de cube de décision et si Start Value n’est pas NULL, la dimension est
dite "paginée" ou "perforée de manière permanente". Il n’est possible d’accéder
aux données que pour une seule valeur à la fois de cette dimension même s’il
est possible par code d’accéder séquentiellement à plusieurs valeurs. Il n’est pas
possible d’ouvrir ou de pivoter une telle dimension.
Les données de dimensions comportant un grand nombre de valeurs différentes
consomment beaucoup de mémoire. En paginant de telles dimensions, il est
possible d’afficher le récapitulatif pour une seule valeur à la fois. Les
informations affichées ainsi sont plus lisibles et la gestion mémoire est simplifiée.

Utilisation de composants d’aide à la décision 27-23


27-24 Guide du développeur
Partie

III
Ecriture d’applications
Part III

distribuées
Les chapitres de cette section présentent les concepts nécessaires à la construction
d’applications distribuées sur un réseau local ou sur internet.
Remarque Les composants décrits dans le chapitre 28 sont disponibles avec l’édition
Entreprise de Delphi. Les composants application serveur Web traités au
chapitre 29 et les composants socket traités au chapitre 30 sont disponibles avec
les éditions Professionnelle et Entreprise.

Ecriture d’applications distribuées


Chapitre

Ecriture d’applications
Chapter 28
28
CORBA
Delphi fournit des experts et des classes qui facilitent la création d’applications
distribuées basées sur l’architecture Common Object Request Broker Architecture
(CORBA). CORBA est une spécification adoptée par l’Object Management Group
(OMG) pour faire face à la complexité du développement d’applications objet
distribuées.
Comme son nom l’indique, CORBA propose une approche orientée objet de
l’écriture d’applications distribuées. Cette approche est à opposer à l’approche
orientée message telle qu’elle est décrite pour les applications HTTP dans le
chapitre 29, “Création d’applications serveur pour Internet”. Avec CORBA, les
applications serveur mettent en oeuvre des objets qui peuvent être utilisés à
distance par des applications client au travers d’interfaces bien définies.
Remarque COM propose une autre approche orientée objet aux applications distribuées.
Pour plus d’informations sur COM, voir chapitre 44, “Présentation
des technologies COM”. Toutefois, à la différence de COM, CORBA est une
norme qui s’applique à des plates-formes autres que Windows. Cela signifie que
vous pouvez écrire à l’aide de Delphi des clients ou des serveurs CORBA
capables de communiquer avec des applications orientées CORBA s’exécutant sur
d’autres plates-formes.
La spécification CORBA définit la manière dont les applications client
communiquent avec des objets installés sur un serveur. Cette communication est
gérée par un Object Request Broker (ORB). La prise en charge de CORBA par
Delphi se base sur VisiBroker pour C++ ORB (Version 3.3.2) qui offre une
enveloppe spéciale (orbpas.dll) exposant un sous-ensemble de la fonctionnalité
ORB aux applications Delphi.

Ecriture d’applications CORBA 28-1


Vue générale d’une application CORBA

En plus de la technologie ORB de base qui permet aux clients de communiquer


avec des objets situés sur des machines serveur, la norme CORBA définit un
certain nombre de services standard. Comme ces services utilisent des interfaces
bien définies, les développeurs peuvent écrire des clients basés sur ces services
même si les serveurs sont écrits par des fournisseurs différents.

Vue générale d’une application CORBA


Si vous utilisez déjà la programmation orientée objet, CORBA simplifie la
conception d’applications distribuées car il vous permet quasiment d’utiliser des
objets distants comme s’ils étaient locaux. En effet, la conception d’une application
CORBA est tout à fait similaire à celle de toute autre application orientée objet, mis
à part qu’elle inclut une couche supplémentaire pour la gestion des
communications réseau lorsqu’un objet réside sur une machine différente. Cette
couche supplémentaire est gérée par des objets spéciaux appelés stubs et squelettes.
Figure 28.1 Structure d’une application CORBA

Sur les clients CORBA, le stub agit comme proxy pour un objet qui peut être
implémenté par le même processus, un autre processus ou une autre machine (un
autre service). Le client interagit avec le stub comme tout autre objet si l’interface
était mise en oeuvre par n’importe quel autre objet. Pour plus d’informations sur
l’utilisation d’interfaces, voir “Utilisation des interfaces” à la page 3-15
Toutefois, à la différence de la plupart des objets qui mettent en œuvre des
interfaces, le stub gère les appels d’interfaces en appelant le logiciel ORB installé
sur la machine client. L’ORB VisiBroker utilise un Smart Agent (osagent) qui
s’exécute quelque part dans le réseau local. Le Smart Agent est un service
d’annuaire distribué dynamique qui localise un serveur disponible fournissant la
mise en oeuvre réelle de l’objet.
Sur le serveur CORBA, le logiciel ORB transmet les appels d’interfaces à un
squelette généré automatiquement. Le squelette communique avec le logiciel ORB
par l’intermédiaire du Basic Object Adaptor (BOA). Il utilise le BOA pour
recenser l’objet auprès du Smart Agent, indique la portée de l’objet (s’il peut être
utilisé sur des machines distantes) et indique quand les objets sont instanciés et
prêts à répondre aux clients.

28-2 Guide du développeur


Vue générale d’une application CORBA

Stubs et squelettes
Les stubs et les squelettes fournissent le mécanisme qui permet aux applications
CORBA d’effectuer le marshaling des appels d’interfaces. Le Marshaling :
• Accueille un pointeur d’interface dans le processus du serveur et met ce
pointeur à la disposition du code du processus client.
• Transfère les arguments d’un appel d’interface tels qu’ils sont transmis par le
client et les place dans l’espace du processus de l’objet distant.
A chaque appel d’interface, l’appelant place les arguments sur la pile et effectue
un appel de fonction par le biais du pointeur d’interface. Si l’objet ne se trouve
pas dans le même espace de processus que le code qui appelle son interface,
l’appel est transmis à un stub appartenant au même espace de processus. Le stub
écrit les arguments dans un tampon de marshaling et transmet l’appel à l’objet
distant dans une structure. Le squelette serveur dépouille cette structure, empile
les arguments et appelle l’implémentation de l’objet. Fondamentalement, le
squelette recrée l’appel du client dans son propre espace d’adressage.
Pour tout appel d’interface, l’appelant empile les arguments et effectue un appel
de fonction par l’intermédiaire du pointeur d’interface. Si l’objet n’est pas dans le
même espace de processus que le code qui appelle son interface, l’appel est
transmis à un stub qui se trouve dans le même espace de processus.
Les stubs et les squelettes sont créés automatiquement lors de la définition de
l’interface de l’objet. Leurs définitions sont créées dans l’unité _TLB créée lors de
la définition de l’interface. Vous pouvez visualiser cette unité en la sélectionnant
dans la clause uses de votre unité d’implémentation puis en appuyant sur Ctrl-
Entrée. Pour plus d’informations sur la définition de l’interface de l’objet, voir
“Définition d’interfaces d’objets” à la page 28-6.

Utilisation de Smart Agents


Le Smart Agent (osagent) est un service d’annuaire dynamique et distribué qui
localise un serveur disponible implémentant un objet. Si plusieurs serveurs sont
dans ce cas, le Smart Agent met en oeuvre un équilibrage de charge. Il protège
également contre les défaillances d’un serveur en tentant de redémarrer le
serveur lorsqu’une connexion échoue ou, si nécessaire, en localisant un serveur
sur un autre système hôte.
Un Smart Agent doit être démarré sur au moins un système hôte de votre réseau
local, le terme réseau local désignant un réseau au sein duquel il est possible
d’envoyer un message de diffusion. L’ORB localise un Smart Agent à l’aide d’un
message de diffusion. Si le réseau inclut plusieurs Smart Agents, l’ORB utilise le
premier qui répond. Après avoir localisé le Smart Agent, l’ORB utilise un
protocole UDP point-à-point pour communiquer avec le Smart Agent. Le
protocole UDP consomme moins de ressources réseau qu’une connexion TCP.

Ecriture d’applications CORBA 28-3


Vue générale d’une application CORBA

Lorsqu’un réseau inclut plusieurs Smart Agents, chaque Smart Agent reconnaît
un sous-ensemble des objets disponibles et communique avec les autres Smart
Agents pour localiser les objets qu’il ne peut pas reconnaître directement. En cas
de terminaison imprévue d’un Smart Agent, les objets dont il garde la trace sont
à nouveau automatiquement recensés avec un autre Smart Agent disponible.
Pour plus de détails sur la configuration et l’utilisation des smart agents dans
vos réseaux locaux, voir “Configuration de Smart Agents” à la page 28-19.

Activation d’applications serveur


Lorsque l’application serveur démarre, elle informe l’ORB (par l’intermédiaire du
Basic Object Adaptor) des objets susceptibles d’accepter des appels client. Ce
code servant à initialiser l’ORB et à l’informer que le serveur est opérationnel est
ajouté automatiquement à votre application par l’expert que vous utilisez pour
démarrer votre application serveur CORBA.
Les applications serveur CORBA sont généralement démarrées manuellement.
Vous pouvez toutefois utiliser l’Object Activation Daemon (OAD) pour démarrer
vos serveurs ou n’instancier leurs objets que lorsque les clients ont besoin de les
utiliser.
Pour utiliser l’OAD, vous devez recenser vos objets par son intermédiaire.
Lorsque vous recensez vos objets avec l’OAD, celui-ci stocke l’association entre
vos objets et l’application serveur qui les met en œuvre dans une base de
données appelée le référentiel d’implémentation (Implementation Repository).
Lorsqu’il existe une entrée pour votre objet dans le référentiel d’implémentation,
l’OAD simule votre application pour l’ORB. Lorsqu’un client interroge l’objet,
l’ORB contacte l’OAD comme s’il s’agissait de l’application serveur. L’OAD
achemine ensuite la requête client vers le serveur réel, en lançant l’application si
nécessaire.
Pour des détails sur le recensement de vos objets avec l’OAD, voir “Recensement
d’interfaces avec l’Object Activation Daemon” à la page 28-10.

Liaison dynamique d’appels d’interfaces


Habituellement, les clients CORBA utilisent une liaison statique lors d’appels
d’interfaces d’objets sur le serveur. Cette approche présente de nombreux
avantages, parmi lesquels de meilleures performances et un contrôle des types au
moment de la compilation. Il est toutefois des cas où vous ne pouvez connaître
les interfaces à utiliser qu’au moment de l’exécution. Dans ces situations, Delphi
vous permet d’effectuer une liaison dynamique avec les interfaces au moment de
l’exécution.
Pour pouvoir bénéficier de la liaison dynamique, vous devezrecenser vos
interfaces avec le référentiel d’interface , à l’aide de l’utilitaire idl2ir.
“Recensement d’interfaces avec le référentiel d’interfaces” à la page 28-9 décrit la
manière de procéder.

28-4 Guide du développeur


Ecriture de serveurs CORBA

Pour des détails sur la manière d’utiliser la liaison dynamique dans vos
applications client CORBA, voir “Utilisation de l’interface d’appel dynamique” à
la page 28-14.

Ecriture de serveurs CORBA


Deux experts sur la page Multi-niveaux de la boîte de dialogue Nouveaux
éléments vous permettent de créer des serveurs CORBA :
• L’expert Module de données CORBA vous permet de créer un serveur
CORBA pour une application de base de données à plusieurs niveaux.
• L’expert Objet CORBA vous permet de créer un serveur CORBA arbitraire.
En outre, vous pouvez facilement convertir un serveur Automation existant en
serveur CORBA en cliquant avec le bouton droit de la souris, puis en choisissant
Exposer comme objet CORBA. En convertissant un serveur Automation en objet
CORBA, vous créez une application capable de servir simultanément des clients
COM et des clients CORBA.

Utilisation des experts CORBA


Pour démarrer l’expert, choisissez Fichier|Nouveau pour afficher la boîte de
dialogue Nouveaux éléments. Sélectionnez la page Multi-niveaux et double-
cliquez sur l’expert approprié.
Vous devez spécifier un nom de classe pour votre objet CORBA. Il s’agit du nom
de base d’un descendant de TCorbaDataModule ou TCorbaImplementation créé par
votre application. Il s’agit aussi du nom de base de l’interface pour cette classe.
Par exemple, si vous spécifiez le nom de classe MyObject, l’expert crée une
nouvelle unité déclarant TMyObject qui implémente l’interface IMyObject.
L’expert vous permet d’indiquer comment vous souhaitez que votre application
serveur crée des instances de cet objet. Vous pouvez choisir Partagé ou Instance
par client.
• Si vous choisissez Partagé, votre application crée une instance unique de
l’objet qui gère toutes les requêtes client. Il s’agit du modèle utilisé dans le
développement CORBA traditionnel. Comme l’instance d’objet unique est
partagée par tous les clients, elle ne doit pas se baser sur des informations
d’état persistantes telles que des valeurs de propriétés.
• Si vous choisissez Instance par client, une nouvelle instance de l’objet est créée
pour chaque connexion client. Cette instance persiste jusqu’à ce qu’une durée
donnée s’écoule sans qu’aucun message ne soit émis par le client. Ce modèle
vous permet d’utiliser des informations d’état persistantes, car il ne peut pas y
avoir d’interférence entre les propriétés des différents clients. Toutefois, les
applications client doivent appeler le serveur suffisamment fréquemment afin
que l’objet serveur ne dépasse le délai d’attente.

Ecriture d’applications CORBA 28-5


Ecriture de serveurs CORBA

Remarque Le modèle instance par client n’est pas typique de la plupart des
développements CORBA, mais fonctionne plutôt comme le modèle COM, où la
durée de vie d’un objet serveur est gérée par l’usage du client. Ce modèle
permet à Delphi de créer des serveurs qui agissent simultanément comme des
serveurs CORBA et COM.
En plus du modèle d’instanciation, vous devez spécifier le modèle de thread.
Vous pouvez choisir Thread unique ou Multi-thread.
• Si vous choisissez Thread unique, chaque instance d’objet ne recevra qu’une
requête client à la fois. Vous pourrez accéder en toute sécurité aux données
d’instance de votre objet (propriétés ou champs). Toutefois, vous devrez vous
prémunir contre les conflits de threads lors de l’utilisation de variables ou
d’objets globaux.
• Si vous choisissez Multi-thread, chaque connexion client disposera de sa
propre thread dédiée. Toutefois, votre application pourra être appelée
simultanément par plusieurs clients, chacun sur une thread séparée. Vous
devrez vous prémunir contre les accès simultanés aux données d’instance ainsi
qu’à la mémoire globale. L’écriture de serveurs Multi-thread est complexe lors
de l’utilisation d’une instance d’objet partagée, car vous devez vous prémunir
contre les conflits de threads pour toutes les données et tous les objets
contenus dans votre application.

Définition d’interfaces d’objets


Avec les outils CORBA traditionnels, les interfaces d’objets doivent être définies
séparément de l’application, à l’aide du langage CORBA Interface Definition
Language (IDL). Vous devez ensuite exécuter un utilitaire pour générer le code
des stubs et des squelettes à partir de cette définition. Delphi génère
automatiquement le stub, le squelette et l’IDL. Vous pouvez facilement modifier
votre interface à l’aide de l’éditeur de bibliothèque de types pour que Delphi
mette automatiquement à jour les fichiers source appropriés. Pour plus
d’informations sur la définition d’interfaces à l’aide de l’éditeur de bibliothèque
de types, voir le chapitre 49, “Utilisation des bibliothèques de types.”
L’éditeur de bibliothèque de types est également utilisé pour la définition de
bibliothèques de types basées sur COM. C’est la raison pour laquelle il inclut un
grand nombre d’options et de contrôles qui ne conviennent pas aux applications
CORBA. Si vous tentez d’utiliser ces options (par exemple, en spécifiant un
numéro de version ou un fichier d’aide), vos informations seront ignorées. Si
vous créez un serveur COM Automation que vous présentez par la suite en tant
que serveur CORBA, ces informations s’appliqueront à votre serveur dans son
rôle en tant que serveur Automation.
Dans l’éditeur de bibliothèque de types, vous pouvez définir votre interface en
utilisant la syntaxe Pascal Objet ou l’IDL Microsoft utilisé pour les objets COM.
Spécifiez le langage que vous souhaitez utiliser lors de la définition de vos
interfaces sur la page Bibliothèques de types de la boîte de dialogue Options
d’environnement. Si vous choisissez d’utiliser l’IDL, rappelez-vous que le langage

28-6 Guide du développeur


Ecriture de serveurs CORBA

IDL de Microsoft est légèrement différent de l’IDL CORBA. Pour la définition de


votre interface, vous êtes limité aux types énumérés dans le tableau ci-dessous :

Tableau 28.1 Types autorisés dans une interface CORBA


Type Détails
ShortInt Entier signé sur 8 bits
Byte Entier non signé sur 8 bits
SmallInt Entier signé sur 16 bits
Word Entier non signé sur 16 bits
Longint, Integer Entier signé sur 32 bits
Cardinal Entier non signé sur 32 bits
Single Valeur flottante sur 4 octets
Double Valeur flottante sur 8 octets
TDateTime Transmis en tant que valeur Double
PWideChar Chaîne Unicode
String, PChar Le type String doit être transtypé en PChar
Variant Transmis en tant que CORBA Any. C’est la seule méthode pour
transmettre une valeur Array ou Currency.
Boolean Transmis en tant que CORBA_Boolean (Byte)
Object Reference Transmis en tant qu’interface CORBA
ou interface
Types énumérés Transmis en tant qu’Integer

Remarque Au lieu d’utiliser l’éditeur de bibliothèque de types, vous pouvez ajouter un


élément à votre interface en cliquant avec le bouton droit de la souris dans
l’éditeur de code puis en choisissant Ajouter à l’interface. Vous aurez toutefois
besoin d’utiliser l’éditeur de bibliothèque de types pour enregistrer un fichier
.IDL pour votre interface.
Vous ne pouvez pas ajouter de propriétés qui utilisent des paramètres (même si
vous pouvez ajouter des méthodes get et set pour ces propriétés). Certains types
(tels que les tableaux, les valeurs Int64 ou les types Currency) doivent être
spécifiés en tant que Variants. Les enregistrements ne sont pas pris en charge
dans la version Client/Serveur.
Votre définition d’interface se reflète dans l’unité stub-et-squelette générée
automatiquement. Cette unité est mise à jour lorsque vous choisissez Rafraîchir
dans l’éditeur de bibliothèque de types ou lorsque vous utilisez la commande
Ajouter à l’interface. Cette unité générée automatiquement est ajoutée à la clause
uses de votre unité d’implémentation. Ne modifiez pas l’unité stub-et-squelette.
En plus de l’unité stub-et-squelette, la modification de l’interface met à jour votre
unité d’implémentation en ajoutant des déclarations pour vos membres
d’interface et en fournissant des implémentations vides pour les méthodes. Vous
pouvez ensuite modifier cette unité d’implémentation afin de fournir un code
significatif pour le corps de chaque nouvelle méthode d’interface.

Ecriture d’applications CORBA 28-7


Ecriture de serveurs CORBA

Remarque Vous pouvez enregistrer un fichier .IDL CORBA pour votre interface en cliquant
sur le bouton Exporter dans l’éditeur de bibliothèque de types. Spécifiez que le
fichier .IDL doit utiliser l’IDL CORBA et non l’IDL Microsoft. Utilisez ce fichier
.IDL pour recenser votre interface ou pour générer des stubs et des squelettes
pour d’autres langages.

Code généré automatiquement


Lorsque vous définissez des interfaces d’objets CORBA , deux fichiers d’unités
sont automatiquement mis à jour conformément à vos définitions d’interfaces.
Le premier est l’unité stub-et-squelette. Son nom est de la forme
MyInterface_TLB.pas. Cette unité définit la classe stub uniquement utilisée par
les applications client, mais contient également la déclaration de vos types
d’interface et de vos classes squelettes. Ce fichier ne doit pas être modifié
directement. Toutefois, cette unité est automatiquement ajoutée à la clause uses
de votre unité d’implémentation.
L’unité stub-et-squelette définit un objet squelette pour chaque interface prise en
charge par votre serveur CORBA. L’objet squelette dérive de TCorbaSkeleton et
gère les détails du marshaling des appels d’interfaces. Il ne met pas en oeuvre
les interfaces que vous définissez. A la place, son constructeur accepte une
instance d’interface qu’il utilise pour gérer tous les appels d’interfaces.
Le deuxième fichier mis à jour est l’unité d’implémentation. Par défaut, son nom
est de la forme unit1.pas, mais vous pouvez le changer pour adopter un nom
plus parlant. C’est ce fichier qui est à modifier.
Pour chaque interface CORBA définie, une définition de classe d’implémentation
est automatiquement ajoutée à votre unité d’implémentation. Le nom de la classe
d’implémentation est basé sur le nom de l’interface. Par exemple, si l’interface
s’appelle IMyInterface, la classe d’implémentation se nommera TMyInterface. Du
code est ajouté à l’implémentation de cette classe pour chaque méthode que vous
ajoutez à l’interface. Vous devez remplir le corps de ces méthodes pour définir
entièrement la classe d’implémentation.
En outre, vous remarquerez que du code est ajouté à la section d’initialisation de
votre unité d’implémentation. Ce code crée un objet TCorbaFactory pour chaque
objet d’interface que vous présentez à des clients CORBA. Lorsque des clients
appellent votre serveur CORBA, l’objet fabrique CORBA crée ou localise une
instance de votre classe d’implémentation et la transmet en tant qu’interface au
constructeur de la classe squelette correspondante.
Remarque L’emploi des objets fabrique qui créent indirectement vos objets serveur CORBA
n’est pas typique de la plupart des développements CORBA. Il suit plutôt le
modèle utilisé par les applications serveur COM et permet à Delphi de
construire des serveurs qui agissent simultanément comme des serveurs COM et
CORBA. Les fabriques permettent aux serveurs CORBA d’implémenter le modèle
instance par client. Les clients des serveurs CORBA construits avec Delphi
utilisent la fonction CorbaFactoryCreateStub qui gère les instructions de fabrique
sur le serveur CORBA pour créer un objet CORBA.

28-8 Guide du développeur


Ecriture de serveurs CORBA

Recensement d’interfaces serveur


Bien qu’il ne soit pas nécessaire de recenser vos interfaces serveur si vous
n’utilisez qu’une liaison statique (lors de la compilation) des appels client dans
vos objets serveur, le recensement de vos interfaces est fortement recommandé.
Deux utilitaires vous permettent de recenser vos interfaces :
• Le référentiel d’interfaces (Interface Repository). Grâce au recensement avec
le référentiel d’interface, les clients peuvent bénéficier de la liaison dynamique.
Cela permet à votre serveur de répondre aux clients non écrits en Delphi s’ils
utilisent l’interface d’appel dynamique (DII). Pour plus d’informations sur
l’utilisation de DII, voir “Utilisation de l’interface d’appel dynamique” à la
page 28-14. Le recensement avec le référentiel d’interface est aussi un moyen
pratique pour permettre à d’autres développeurs de visualiser vos interfaces
lorsque vous écrivez des applications client.
• L’Object Activation Daemon. Lors d’un recensement avec l’Object Activation
Daemon (OAD), il n’est pas nécessaire de lancer votre serveur ou d’instancier
vos objets tant qu’aucun client n’en a besoin. Cela permet d’économiser les
ressources de votre système serveur.

Recensement d’interfaces avec le référentiel d’interfaces


Vous pouvez créer un référentiel d’interfaces pour vos interfaces en exécutant le
serveur de référentiel d’interfaces. Vous devez d’abord enregistrer le fichier .IDL
pour votre interface. Pour cela, choisissez Voir|Bibliothèques de types, puis dans
l’éditeur de bibliothèque de types, cliquez sur le bouton Exporter pour exporter
votre interface en tant que fichier CORBA .IDL.
Lorsque vous disposez d’un fichier .IDL pour votre interface, vous pouvez
exécuter le serveur de référentiel d’interfaces en utilisant la syntaxe suivante :
irep [-console] NomRI [fichier.idl]
Les arguments d’irep sont décrits dans le tableau ci-dessous :

Tableau 28.2 Arguments d’irep


Argument Description
-console Démarre le serveur de référentiel d’interfaces en tant qu’application
console. Par défaut, le serveur de référentiel d’interfaces fonctionne
en tant qu’application Windows.
NomRI Nom du référentiel d’interfaces. Pendant que le serveur est en
fonctionnement, les clients utilisent ce nom pour se lier au référentiel
d’interfaces afin de pouvoir obtenir des informations d’interfaces
pour DII ou de pouvoir recenser d’autres interfaces.
fichier.idl Fichier .IDL décrivant le contenu initial du référentiel d’interfaces. Si
vous ne spécifiez pas de nom de fichier, le référentiel d’interfaces est
vide au départ. Vous pouvez ajouter par la suite des interfaces en
utilisant les menus du serveur de référentiel d’interfaces ou l’utilitaire
idl2ir.

Ecriture d’applications CORBA 28-9


Ecriture de serveurs CORBA

Une fois le serveur de référentiel d’interfaces en fonctionnement, vous pouvez


ajouter d’autres interfaces en choisissant Fichier|Charger et en spécifiant un
nouveau fichier .IDL. Toutefois, si le nouveau fichier .IDL contient des entrées
qui correspondent à une entrée .IDL existante, le nouveau fichier .IDL est rejeté.
Vous pouvez enregistrer à tout moment le contenu du référentiel d’interfaces
dans un fichier .IDL en choisissant Fichier|Enregistrer ou Fichier|Enregistrer
sous. Ainsi, après avoir quitté le référentiel d’interfaces, vous pourrez le
redémarrer plus tard avec le fichier enregistré de manière à ne pas avoir à
réimporter tous les changements dans le fichier .IDL initial.
Vous pouvez aussi recenser d’autres interfaces avec le référentiel d’interfaces à
l’aide de l’utilitaire idl2ir. Pendant que le serveur de référentiel d’interfaces est
en fonctionnement, démarrez l’utilitaire idl2ir en utilisant la syntaxe suivante :
idl2ir [-ir NomRI] {-replace} fichier.idl
Les arguments d’idl2ir sont décrits dans le tableau suivant :

Tableau 28.3 Arguments d’idl2ir


Argument Description
-ir NomRI Ordonne à l’utilitaire de se lier à l’instance de référentiel d’interfaces
nommée NomRI. Si cet argument n’est pas spécifié, idl2ir se lie à
n’importe quel référentiel d’interfaces renvoyé par le smart agent.
-replace Ordonne à l’utilitaire de remplacer les éléments du référentiel d’interfaces
par les éléments correspondants présents dans fichier.idl. Si -replace n’est
pas spécifié, l’interface est ajoutée en entier au référentiel, sauf s’il existe
des éléments correspondants, auquel cas l’utilitaire rejette en totalité le
fichier .IDL. Si -replace est spécifié, les éléments qui ne correspondent
pas sont rejetés.
fichier.idl Spécifie le fichier .IDL qui contient les mises à jour du référentiel
d’interfaces.

Les entrées d’un référentiel d’interfaces ne peuvent pas être supprimées tant que
le serveur du référentiel d’interfaces fonctionne. Pour supprimer un élément,
vous devez arrêter le serveur du référentiel d’interfaces, générer un nouveau
fichier .IDL puis démarrer le serveur du référentiel d’interfaces en spécifiant le
fichier .IDL mis à jour.

Recensement d’interfaces avec l’Object Activation Daemon


Pour que vous puissiez recenser une interface avec l’Object Activation Daemon
(OAD), il faut que le programme en ligne de commande OAD soit en train de
fonctionner sur au moins un ordinateur de votre réseau local. Pour démarrer
l’OAD, utilisez la syntaxe suivante :
oad [options]

28-10 Guide du développeur


Ecriture de serveurs CORBA

L’utilitaire OAD accepte les arguments de ligne de commande suivants :

Tableau 28.4 Arguments d’OAD


Argument Description
-v Active le mode verbeux.
-f Stipule que le processus ne doit pas échouer si un autre OAD est en cours
d’exécution sur ce système hôte.
-t<n> Spécifie le nom de secondes pendant lesquelles l’OAD attendra qu’un serveur
déclenché active l’objet demandé. Le délai par défaut est de 20 secondes.
L’initialisation de cette valeur à 0 provoque une attente indéfinie de la part
de l’OAD. Si le processus serveur déclenché n’active pas l’objet demandé
avant l’expiration de ce délai, l’OAD met fin au processus serveur et renvoie
une exception.
-C Permet à l’OAD de s’exécuter en mode console s’il a été installé en tant que
service NT.
-k Stipule que le processus enfant d’un objet doit être tué lorsque le
recensement de tous ses objets est annulé avec l’OAD.
-? Décrit ces arguments.

Une fois l’OAD en fonctionnement, vous pouvez recenser vos interfaces d’objets
en utilisant le programme en ligne de commande oadutil. Vous devez tout
d’abord exporter le fichier .IDL pour vos interfaces. Pour cela, cliquez sur le
bouton Exporter dans l’éditeur de bibliothèque de types et enregistrez la
définition des interfaces en tant que fichier .IDL CORBA.
Ensuite, recensez les interfaces en utilisant le programme oadutil avec la syntaxe
suivante :
oadutil reg [options]
Les arguments disponibles lors du recensement d’interfaces à l’aide d’oadutil
sont les suivants :

Tableau 28.5 Arguments d’oadutil reg


Argument Description
-i <nom interface> Spécifie un fichier d’interface IDL particulier. Vous devez spécifier
l’interface à recenser en utilisant cette option ou l’option -r.
-r <id référentiel> Spécifie une interface particulière à l’aide de son id référentiel. L’id
référentiel est un identifiant unique associé à l’interface. Vous devez
spécifier l’interface à recenser en utilisant cette option ou l’option -i.
-o <nom objet> Spécifie le nom de l’objet qui met en oeuvre l’interface. Cette option
est obligatoire.
-cpp <nom fichier> Spécifie le nom de votre exécutable serveur. Cette option est
obligatoire.
-host <nom hôte> Spécifie un système hôte distant sur lequel l’OAD est en train de
fonctionner. (facultatif)
-verbose Démarre l’utilitaire en mode verbeux. Les messages sont envoyés vers
stdout. (facultatif)

Ecriture d’applications CORBA 28-11


Ecriture de serveurs CORBA

Tableau 28.5 Arguments d’oadutil reg


Argument Description
-d<données Spécifie les données de référence transmises à l’application serveur
référence> lors de son activation. (facultatif)
-a arg1 Spécifie les arguments de ligne de commande transmis à l’application
serveur. Il est possible de transmettre plusieurs arguments en
utilisant plusieurs paramètres -a. (facultatif)
-e env1 Spécifie les variables d’environnement transmises à l’application
serveur. Il est possible de transmettre plusieurs arguments en
utilisant plusieurs paramètres -e. (facultatif)

Par exemple, la ligne suivante recense une interface d’après son id référentiel :
oadutil reg -r IDL:MyServer/MyObjectFactory:1.0 -o TMyObjectFactory -cpp MyServer.exe -p
unshared
Remarque Vous pouvez obtenir l’ID référentiel de votre interface en examinant le code
ajouté à la section d’initialisation de votre unité d’implémentation. Il s’agit du
troisième argument de l’appel à TCorbaFactory.Create.
Lorsqu’une interface devient indisponible, vous devez annuler son recensement.
Un objet dont le recensement est annulé ne peut plus être activé
automatiquement par l’OAD si un client demande l’objet. Seuls les objets qui ont
été préalablement recensés à l’aide d’oadutil reg peuvent voir leur recensement
annulé.
Pour annuler le recensement d’interfaces, utilisez la syntaxe suivante :
oadutil unreg [options]
Les arguments disponibles pour l’annulation du recensement d’interfaces à l’aide
d’oadutil sont les suivants :

Tableau 28.6 Arguments d’oadutil unreg


Argument Description
-i <nom interface> Spécifie un nom d’interface d’IDL particulier. Vous devez spécifier
l’interface dont le recensement doit être annulé en utilisant cette
option ou l’option -r.
-r <id référentiel> Spécifie une interface particulière par son id référentiel. L’id
référentiel est un identifiant unique associé à l’interface lorsqu’elle est
recensée à l’aide du référentiel d’interfaces. Vous devez spécifier
l’interface dont le recensement doit être annulé en utilisant cette
option ou l’option -i.
-o <nom objet> Spécifie le nom de l’objet qui prend en charge l’interface. Si vous ne
spécifiez pas de nom d’objet, le recensement est annulé pour tous les
objets qui prennent en charge l’interface spécifiée.
-host <nom hôte> Spécifie un hôte distant sur lequel l’OAD est en cours d’exécution.
(facultatif)
-verbose Démarre l’utilitaire en mode verbeux. Les messages sont envoyés à
stdout. (facultatif)
-version Affiche le numéro de version pour oadutil.

28-12 Guide du développeur


Ecriture de clients CORBA

Ecriture de clients CORBA


Lorsque vous écrivez un client CORBA, la première étape consiste à s’assurer
que l’application client peut dialoguer avec le logiciel ORB sur la machine client.
Pour ce faire, ajoutez simplement CorbaInit à la clause uses de votre fichier
d’unité.Ecrivez ensuite votre application comme vous le feriez pour n’importe
quelle autre application dans Delphi. Toutefois, pour utiliser des objets définis
dans l’application serveur, il ne faut pas travailler directement avec une instance
d’objet. Il faut plutôt obtenir une interface pour l’objet et la manipuler. Vous
pouvez obtenir l’interface de deux manières, selon que vous voulez utiliser la
liaison à l’avance (statique) ou la liaison retardée (dynamique).
Pour utiliser la liaison à l’avance , vous devez ajouter une unité stub-et-squelette
à votre application client. L’unité stub-et-squelette est créée automatiquement
lorsque vous recensez l’interface serveur. L’utilisation de la liaison à l’avance est
plus rapide que l’utilisation de la liaison dynamique, et elle présente d’autres
avantages tels que le contrôle des types lors de la compilation et le complément
de code.
Il est toutefois des cas où vous ne savez pas jusqu’à l’exécution quel objet
(interface) vous souhaitez utiliser. Pour ces cas, vous pouvez utiliser la liaison
dynamique. La liaison dynamique ne requiert pas d’unité stub, mais elle requiert
que toutes les interfaces d’objets distants utilisées soit recensées avec un
référentiel d’interfaces fonctionnant dans le réseau local.
Conseil Vous pouvez utiliser la liaison dynamique pour écrire des clients CORBA pour
des serveurs qui ne sont pas écrits en Delphi. Ainsi, vous n’avez pas à écrire
votre propre classe stub pour le marshaling des appels d’interfaces.

Utilisation des stubs


Les classes stub sont générées automatiquement quand vous définissez une
interface CORBA. Elles sont définies dans une unité stub-et-squelette, dont le
nom a la forme NomBase_TLB (dans un fichier dont le nom a la forme
NomBase_TLB.pas).
Lors de l’écriture d’un client CORBA, vous ne modifiez pas le code de l’unité
stub-et-squelette. Ajoutez l’unité stub-et-squelette dans la clause uses de l’unité
qui requiert une interface pour un object sur le serveur CORBA.Pour chaque
objet serveur, l’unité stub-et-squelette contient une définition d’interface et une
définition de classe pour une classe stub correspondante. Par exemple, si le
serveur définit une classe d’objet TServerObj, l’unité stub-et-squelette inclut une
définition pour l’interface IServerObj et pour une classe stub TServerObjStub. La
classe stub est un descendant de TCorbaDispatchStub et met en oeuvre son
interface correspondante en effectuant le marshaling des appels au serveur
CORBA. En plus de la classe stub, l’unité stub-et-squelette définit une classe
fabrique de stubs pour chaque interface. Cette classe fabrique de stubs n’est
jamais instanciée : elle définit une méthode de classe unique.

Ecriture d’applications CORBA 28-13


Ecriture de clients CORBA

Dans votre application client, vous ne créez pas directement d’instances de la


classe stub lorsque vous avez besoin d’une interface pour l’objet sur le serveur
CORBA. Appelez à la place la méthode CreateInstance de la classe fabrique de
stubs. Cette méthode accepte un argument, un nom d’instance facultatif, et
renvoie une interface à l’instance de l’objet sur le serveur. Par exemple :
var
ObjInterface : IServerObj;
begin
ObjInterface := TServerObjFactory.CreateInstance('');
...
end;
Lorsque vous appelez CreateInstance, cette méthode
1 Obtient une instance d’interface auprès de l’ORB.
2 Utilise cette interface pour créer une instance de la classe de stub.
3 Renvoie l’interface résultante.
Remarque Si vous écrivez un client pour un serveur CORBA qui n’a pas été écrit avec
Delphi, vous devez écrire votre propre descendant de TCorbaStub pour fournir
une prise en charge du marshaling pour votre client. Vous devez ensuite
recenser cette classe stub avec le CORBAStubManager global. Enfin, pour
instancier la classe stub et obtenir l’interface serveur, vous pouvez appeler la
procédure globale BindStub pour obtenir une interface que vous pourrez
transmettre ensuite à la méthode CreateStub du gestionnaire de stub CORBA.

Utilisation de l’interface d’appel dynamique


L’interface d’appel dynamique (Dynamic Invocation Interface, DII) permet aux
applications client d’appeler des objets serveur sans utiliser une classe de stub
effectuant un marshaling explicite des appels d’interfaces. Comme DII doit
encoder toutes les informations de type avant que le client n’envoie une requête
puis décoder cette information sur le serveur, son utilisation est plus lente que
celle d’une classe stub.
Pour pouvoir utiliser DII, il faut que les interfaces serveur soient recensées avec
un référentiel d’interfaces fonctionnant dans le réseau local. Pour davantage
d’informations sur le recensement d’interfaces avec le référentiel d’interfaces, voir
“Recensement d’interfaces avec le référentiel d’interfaces” à la page 28-9.
Pour utiliser DII dans une application client, obtenez une interface serveur et
affectez-la à une variable de type TAny. TAny est un Variant spécial propre à
CORBA . Ensuite, appelez les méthodes de l’interface en utilisant la variable
TAny comme s’il s’agissait d’une instance d’interface. Le compilateur gère les
détails de la transformation de vos appels en requêtes DII.

Obtention de l’interface
Pour obtenir une interface pour des appels DII à liaison différée, utilisez la
fonction globale CorbaBind. CorbaBind accepte l’ID référentiel de l’objet serveur ou
un type d’interface. Elle utilise ces informations pour demander une interface à
l’ORB, et utilise cette dernière pour créer un objet stub.

28-14 Guide du développeur


Ecriture de clients CORBA

Remarque Pour pouvoir appeler CorbaBind, il faut recenser l’association entre le type d’interface
et son ID référentiel avec le CorbaInterfaceIDManager global.
Si votre application client possède une classe stub recensée pour le type d’interface,
CorbaBind crée un stub de cette classe. Dans ce cas, l’interface renvoyée par CorbaBind
peut être utilisée pour une liaison avancée (par un transtypage avec l’opérateur as)
ou une liaison différée (DII). S’il n’existe pas de classe stub recensée pour le type
d’interface, CorbaBind renvoie l’interface sur un objet stub générique. Un objet stub
générique ne peut être utilisé que pour les appels différés (DII).
Pour utiliser l’interface renvoyée par CorbaBind pour des appels DII, affectez-la à une
variable de type TAny :
var
IntToCall: TAny;
begin
IntToCall := CorbaBind('IDL:MyServer/MyServerObject:1.0');
...

Appel d’interfaces avec DII


Lorsqu’une interface a été affectée à une variable de type TAny, l’appeler en utilisant
DII consiste simplement à utiliser la variable comme s’il s’agissait d’une interface :
var
HR, Emp, Payroll, Salary: TAny;
begin
HR := CorbaBind('IDL:CompanyInfo/HR:1.0');
Emp := HR.LookupEmployee(Edit1.Text);
Payroll := CorbaBind('IDL:CompanyInfo/Payroll:1.0');
Salary := Payroll.GetEmployeeSalary(Emp);
Payroll.SetEmployeeSalary(Emp, Salary + (Salary * StrToInt(Edit2.Text) / 100));
end;
Lors de l’utilisation de DII, toutes les méthodes d’interface sont sensibles à la casse.
A la différence des appels liés statiquement, vous devez être sûr que les noms de
méthodes correspondent à la casse utilisée dans la définition d’interface.
Lors de l’appel d’une interface avec DII, chaque paramètre est traité comme une
valeur de type TAny, car les valeurs TAny transportent avec elles leurs informations
de type. Ces informations de type permettent au serveur d’interpréter des
informations de type lorsqu’ils reçoivent l’appel.
Comme les paramètres sont toujours traités comme des valeurs TAny, il n’est pas
nécessaire d’effectuer de conversion explicite vers le type de paramètre approprié.
Ainsi, dans l’exemple précédent, vous pouvez transmettre une chaîne à la place
d’une valeur à virgule flottante comme dernier paramètre pour l’appel à
SetEmployeeSalary :
Payroll.SetEmployeeSalary(Emp, Edit2.Text);

Ecriture d’applications CORBA 28-15


Personnalisation d’applications CORBA

Vous pouvez toujours transmettre directement en paramètres des types simples que
le compilateur convertira en valeurs TAny. Pour les types structurés, vous devez
utiliser les méthodes de conversion de la variable ORB globale pour créer un type
TAny. Le tableau suivant indique la méthode à utiliser pour créer différents types
structurés :

Tableau 28.7 Méthodes ORB pour la création de valeurs TAny structurées


Type structuré Fonction helper
record MakeStructure
array (longueur fixe)
dynamic array (séquence) MakeSequence

Lors de l’utilisation de ces fonctions helper, vous devez spécifier le code


décrivant le type d’enregistrement (record), de tableau (array) ou de séquence
que vous souhaitez créer. Vous pouvez obtenir ce type dynamiquement à partir
d’un ID référentiel en utilisant la méthode FindTypeCode :
var
HR, Name, Emp, Payroll, Salary: TAny;
begin
with ORB do
begin
HR := Bind('IDL:CompanyInfo/HR:1.0');
Name := MakeStructure(FindTypeCode('IDL:CompanyInfo/EmployeeName:1.0',
[Edit1.Text,Edit2,Text]);
Emp := HR.LookupEmployee(Name);
Payroll := Bind('IDL:CompanyInfo/Payroll:1.0');
end;
Salary := Payroll.GetEmployeeSalary(Emp);
Payroll.SetEmployeeSalary(Emp, Salary + (Salary * StrToInt(Edit3.Text) / 100));
end;

Personnalisation d’applications CORBA


Deux variables globales, ORB et BOA, vous permettent de personnaliser la
manière dont votre application interagit avec le logiciel CORBA qui s’exécute sur
votre réseau.
Les applications client utilisent la variable ORB pour configurer le logiciel ORB,
se déconnecter du serveur, se lier à des interfaces et obtenir des représentations
chaînes pour des objets afin de pouvoir afficher des noms d’objets dans
l’interface utilisateur.
Les applications serveur utilisent la variable BOA pour configurer le logiciel
BOA, montrer ou cacher des objets et lire des informations personnalisées
affectées à un objet par des applications client.

28-16 Guide du développeur


Personnalisation d’applications CORBA

Affichage d’objets dans l’interface utilisateur


Lors de l’écriture d’une application client CORBA, vous pouvez présenter aux
utilisateurs les noms des objets serveur CORBA disponibles. Pour cela, vous
devez convertir votre interface d’objet en chaîne. Cette conversion est réalisée par
la méthode ObjectToString globale ORB . Par exemple, le code suivant affiche les
noms de trois objets dans une boîte liste, à partir des instances d’interfaces pour
leurs objets stub correspondants.
var
Dept1, Dept2, Dept3: IDepartment;
begin
Dept1 := TDepartmentFactory.CreateInstance('Sales');
Dept1.SetDepartmentCode(120);
Dept2 := TDepartmentFactory.CreateInstance('Marketing');
Dept2.SetDepartmentCode(98);
Dept3 := TSecondFactory.CreateInstance('Payroll');
Dept3.SetDepartmentCode(49);
ListBox1.Items.Add(ORB.ObjectToString(Dept1));
ListBox1.Items.Add(ORB.ObjectToString(Dept2));
ListBox1.Items.Add(ORB.ObjectToString(Dept3));
end;
En laissant l’ORB créer des chaînes pour vos objets, vous pouvez utiliser la
méthode StringToObject pour inverser cette procédure :
var
Dept: IDepartment;
begin
Dept := ORB.StringToObject(ListBox1.Items[ListBox1.ItemIndex]);
... { traitement quelconque avec le département sélectionné }

Présentation et dissimulation d’objets CORBA


Lorsqu’une application serveur CORBA crée une instance d’objet, elle peut
mettre cet objet à la disposition des clients en appelant la méthode ObjIsReady de
la variable globale BOA.
Tout objet présenté en utilisant ObjIsReady peut être caché par l’application
serveur. Pour cacher un objet, appelez la méthode Deactivate de BOA.
Si le serveur désactive un objet, cela peut invalider une interface d’objet détenue
par une application client. Les applications client peuvent détecter cette situation
en appelant la méthode NonExistent de l’objet stub. NonExistent renvoie True lorsque
l’objet serveur a été désactivé et False si l’objet serveur est toujours disponible.

Transmission d’informations client à des objets serveur


Les objets stub de clients CORBA peuvent envoyer des informations d’identification
au serveur associé en utilisant un TCorbaPrincipal. Un TCorbaPrincipal est un tableau
d’octets représentant des informations sur l’application client. Les objets stub
l’initialisent en utilisant leur méthode SetPrincipal.

Ecriture d’applications CORBA 28-17


Déploiement d’applications CORBA

Lorsque le client CORBA a écrit des données principales dans l’instance de


l’objet serveur, l’objet serveur peut accéder à ces informations en utilisant la
méthode GetPrincipal de BOA.
Comme TCorbaPrincipal est un tableau d’octets, il peut représenter n’importe
quelles données que le développeur pourra juger utile de transmettre. Par
exemple, des clients avec des privilèges spéciaux peuvent envoyer une valeur de
clé que le serveur examinera avant de rendre certaines méthodes disponibles.

Déploiement d’applications CORBA


Après avoir créé des applications client ou serveur et les avoir soigneusement
testées, vous êtes prêt à déployer des applications client sur les postes de travail
des utilisateurs finaux et des applications serveur sur des ordinateurs de type
serveur. La liste suivante décrit les fichiers qui doivent être installés (en plus de
votre application client ou serveur) lorsque vous déployez votre application
CORBA :
• Les bibliothèques ORB doivent être installées sur tous les ordinateurs client et
serveur. Ces bibliothèques se trouvent dans le sous-répertoire Bin du
répertoire dans lequel vous avez installé VisiBroker.
• Si votre client utilise l’interface d’appel dynamique (DII), vous devez exécuter
un serveur de référentiel d’interfaces sur au moins un système hôte dans le
réseau local, le terme “réseau local” désignant un réseau dans lequel il est
possible d’envoyer un message de diffusion. Voir “Recensement d’interfaces
avec le référentiel d’interfaces” à la page 28-9 pour des détails sur la manière
d’exécuter le serveur de référentiel d’interfaces et de recenser les interfaces
appropriées.
• Si votre serveur doit être démarré à la demande, il est nécessaire que l’Object
Activation Daemon (OAD) soit exécuté sur au moins un système hôte dans le
réseau local. Voir “Recensement d’interfaces avec l’Object Activation Daemon”
à la page 28-10 pour des détails sur la manière d’exécuter l’OAD et de
recenser les interfaces appropriées.
• Un Smart Agent (osagent) doit être installé sur au moins un système hôte
dans le réseau local. Vous pouvez déployer le Smart Agent sur plusieurs
ordinateurs.
En outre, il peut être nécessaire d’initialiser les variables d’environnement
suivantes lors du déploiement de votre application CORBA.

Tableau 28.8 Variables d'environnement CORBA


Variable Signification
PATH Vous devez vous assurer que le répertoire qui contient les
bibliothèques ORB se trouve dans le Path.
VBROKER_ADM Spécifie le répertoire qui contient les fichiers de configuration
pour le référentiel d’interfaces, l’Object Activation Daemon et le
Smart Agent.

28-18 Guide du développeur


Déploiement d’applications CORBA

Tableau 28.8 Variables d'environnement CORBA (suite)


Variable Signification
OSAGENT_ADDR Spécifie l’adresse IP de l’ordinateur hôte dont il faut utiliser le
Smart Agent. Si cette variable n’est pas initialisée, l’application
CORBA utilise le premier Smart Agent qui répond à un message
de diffusion.
OSAGENT_PORT Spécifie le port utilisé par un Smart Agent pour répondre aux
requêtes.
OSAGENT_ADDR_FILE Spécifie le chemin complet totalement qualifié d’un fichier
contenant des adresses de Smart Agent dans d’autres réseaux
locaux.
OSAGENT_LOCAL_FILE Spécifie le chemin totalement qualifié d’un fichier contenant des
informations réseau pour un Smart Agent qui s’exécute sur un
système hôte à localisations multiples.
VBROKER_IMPL_PATH Spécifie le répertoire du référentiel d’implémentation (où l’OAD
stocke ses informations).
VBROKER_IMPL_NAME Spécifie le nom de fichier par défaut du référentiel
d’implémentation.

Remarque Pour des informations générales sur le déploiement d’applications, voir le


chapitre 11, “Déploiement des applications”.

Configuration de Smart Agents


Lorsque vous déployez votre application CORBA, il est nécessaire qu’au moins
un smart agent soit en cours d’exécution dans le réseau local. En déployant
plusieurs smart agents dans un réseau local, vous vous protégez d’une
défaillance éventuelle de l’ordinateur sur lequel le smart agent s’exécute.
Le déploiement des smart agents s’effectue de manière à ce qu’ils divisent un
réseau local en domaines ORB séparés. Inversement, vous pouvez connecter des
smart agents sur différents réseaux locaux afin d’élargir le domaine de votre
ORB.

Démarrage du Smart Agent


Pour démarrer le Smart Agent, exécutez l’utilitaire osagent. Vous devez exécuter au
moins un Smart Agent sur un système hôte dans votre réseau local. L’utilitaire
osagent accepte les arguments de ligne de commande suivants :

Tableau 28.9 Arguments osagent


Argument Description
-v Active le mode verbeux. Des messages d’information et de diagnostic sont
écrits dans un fichier journal nommé osagent.log. Ce fichier se trouve dans le
répertoire spécifié par la variable d’environnement VBROKER_ADM.
-p<n> Spécifie le port UDP utilisé par le Smart Agent pour écouter les messages de
diffusion.
-C Permet au Smart Agent de s’exécuter en mode console s’il a été installé en
tant que service NT.

Ecriture d’applications CORBA 28-19


Déploiement d’applications CORBA

Par exemple, tapez la commande suivante dans une boîte DOS ou choisissez
Exécuter à partir du bouton Démarrer :
osagent -p 11000
Cette commande lance le Smart Agent afin qu’il écoute le port UDP 11000 au
lieu d’écouter le port par défaut (14000). Le changement du port utilisé par le
Smart Agent pour écouter les messages de diffusion vous permet de créer
plusieurs domaines ORB.

Configuration de domaines ORB


Il est souvent souhaitable de disposer de plusieurs domaines ORB séparés
s’exécutant au même moment. Un domaine peut se composer de la version de
production d’applications client et d’implémentations d’objets tandis qu’un autre
peut inclure des versions de test des mêmes clients et objets qui n’ont pas encore
été publiés pour une utilisation générale. Si plusieurs développeurs travaillent
dans le même réseau local, chacun d’eux peut souhaiter disposer d’un domaine
ORB dédié afin que les efforts de test des différents développeurs n’entrent pas
en conflit les uns avec les autres.
Figure 28.2 Domaines ORB séparés

La distinction entre plusieurs domaines ORB dans le même réseau peut


s’effectuer en utilisant un numéro de port UDP unique des osagents dans chaque
domaine.
Le numéro de port par défaut (14000) est inscrit dans le registre Windows lors
de l’installation de l’ORB. Pour surcharger cette valeur, initialisez la variable
d’environnement OSAGENT_PORT à une valeur différente. Vous pouvez
surcharger la valeur spécifiée par OSAGENT_PORT en démarrant le Smart Agent
avec l’option -p.

28-20 Guide du développeur


Déploiement d’applications CORBA

Connexion de Smart Agents avec d’autres réseaux locaux


Si vous démarrez plusieurs Smart Agents dans votre réseau local, ils utiliseront
chacun des messages de diffusion UDP. La configuration de vos réseaux locaux
s’effectue en spécifiant la portée de messages de diffusion à l’aide du masque de
sous-réseau IP. Vous pouvez permettre à un Smart Agent de communiquer avec
d’autres réseaux de deux manières :
• En utilisant un fichier agentaddr.
• En utilisant un système hôte à plusieurs localisations.

Utilisation d’un fichier agentaddr


Considérons les deux Smart Agents décrits à la figure suivante. Le Smart Agent
du réseau numéro 1 écoute les messages de diffusion en utilisant l’adresse IP
199.10.9.5. Le Smart Agent du réseau numéro 2 écoute l’adresse IP 101.10.2.6.
Figure 28.3 Deux Smart Agents dans des réseaux locaux séparés

Le Smart Agent du réseau numéro 1 peut contacter le Smart Agent du réseau


numéro 2 s’il parvient à trouver un fichier nommé agentaddr contenant la ligne
suivante :
101.10.2.6
Le Smart Agent recherche ce fichier dans le répertoire spécifié par la variable
d’environnement VBROKER_ADM.

Utilisation d’un système hôte à localisations multiples


Lorsque vous démarrez le Smart Agent sur un système hôte qui possède
plusieurs adresses IP (un système hôte à localisations multiples), il peut fournir
un mécanisme puissant pour faire le lien entre des objets situés dans des réseaux
locaux séparés. Tous les réseaux locaux auxquels le système hôte est connecté
peuvent communiquer avec un même Smart Agent, et relier effectivement les
réseaux locaux sans fichier agentaddr.

Ecriture d’applications CORBA 28-21


Déploiement d’applications CORBA

Toutefois, dans un système à localisations multiples, le Smart Agent n’est pas


capable de déterminer le masque de sous-réseau et l’adresse de diffusion
corrects. Ces valeurs doivent être spécifiées dans un fichier localaddr. Vous
pouvez obtenir les valeurs d’interface réseau appropriées à partir du fichier
localaddr en accédant aux propriétés du protocole TCP/IP à partir du panneau
de configuration réseau (Network Control Panel). Si votre système hôte
fonctionne sous Windows NT, vous pouvez utiliser la commande ipconfig pour
obtenir ces valeurs.
Le fichier localaddr contient une ligne pour chaque combinaison d’adresse IP, de
masque de sous-réseau et d’adresse de diffusion que le Smart Agent peut
utiliser. Voici, par exemple, le contenu d’un fichier localaddr pour un Smart
Agent disposant de deux adresses IP :
216.64.15.10 255.255.255.0 216.64.15.255
214.79.98.88 255.255.255.0 214.79.98.255
Vous pouvez aussi initialiser la variable d’environnement
OSAGENT_LOCAL_FILE avec le chemin totalement qualifié du fichier localaddr.
Cela permet au Smart Agent de localiser ce fichier.

28-22 Guide du développeur


Chapitre

Création d’applications serveur


Chapter 29
29
pour Internet
Delphi vous permet de créer des applications serveur Web en tant
qu’applications CGI ou DLL. Ces applications serveur Web peuvent contenir tout
type de composant non visuel. Des composants spéciaux de la page Internet de
la palette des composants facilitent la création des gestionnaires d’événements
associés à un URI (Uniform Resource Identifier) spécifique et, lorsque le
traitement est terminé, la construction et le transfert au client, par
programmation, de documents HTML.
En règle générale, ce contenu est issu de bases de données. Les composants
Internet vous serviront à gérer automatiquement les connexions aux bases de
données et permettront à une DLL de gérer plusieurs connexions simultanées
aux bases de données sans problèmes de thread.
Ce chapitre décrit ces composants Internet et la création de plusieurs types
d’applications Internet.
Remarque Vous pouvez utiliser des fiches ActiveForm comme applications serveur Internet.
Pour plus d’informations sur les fiches ActiveForm, voir “Génération d’un
contrôle ActiveX basé sur une fiche VCL” à la page 48-7.

Terminologie et standard
La majorité des protocoles contrôlant l’activité Internet sont définis dans des
documents Request for Comment (RFC) gérés par le comité IETF (Internet
Engineering Task Force), comité chargé de l’ingénierie et du développement des
protocoles Internet. Plusieurs documents RFC vous apporteront d’utiles
précisions pour le développement d’applications Internet :
• Le RFC822, “Standard for the format of ARPA Internet text messages,” décrit
la structure et le contenu des en-têtes de messages.

Création d’applications serveur pour Internet 29-1


Terminologie et standard

• Le RFC1521, “MIME (Multipurpose Internet Mail Extensions) Part One:


Mechanisms for Specifying and Describing the Format of Internet Message
Bodies,” décrit la méthode utilisée pour encapsuler et transporter des
messages multiparties et multiformats.
• Le RFC1945, “Hypertext Transfer Protocol — HTTP/1.0,” décrit une méthode
de transfert utilisée pour distribuer des documents hypermédia collaboratifs.
Le comité IETF propose une bibliothèque des documents RFC sur le site Web
www.ietf.cnri.reston.va.us

Composition d’un URL (Uniform Resource Locator)


L’URL est une description complète de l’emplacement d’une ressource sur
Internet. Un URL se compose de plusieurs parties auxquelles une application
peut accéder. Ces différentes parties sont décrites dans la figure suivante :
Figure 29.1 Composants d’un URL

La première partie (qui n'appartient pas techniquement à l'URL) identifie le


protocole (http). Cette partie peut spécifier d'autres protocoles comme https
(secure http), ftp, etc.
La partie Hôte identifie la machine qui exécute le serveur Web et l'application
serveur Web. Bien que cela n'apparaisse pas dans l'illustration précédente, cette
partie peut se substituer au port qui reçoit les messages. Généralement, il n'est
pas nécessaire de spécifier un port, car le protocole implique le numéro de port.
La partie Nom script spécifie le nom de l'application serveur Web. Il s'agit de
l'application à laquelle le serveur Web transmet les messages.
A la suite du nom de script apparaît le chemin. Ce dernier identifie la destination
du message dans l'application serveur Web. Les valeurs du chemin peuvent faire
référence à des répertoires de la machine hôte, aux noms de composants qui
répondent à des messages spécifiques ou à tout autre mécanisme utilisé par
l'application serveur Web pour diviser le traitement des messages entrants.
La partie Requête contient un ensemble de valeurs nommées. Ces valeurs et
leurs noms sont définis par l'application serveur Web.

URI et URL
L’URL est un sous-ensemble d’un URI (Uniform Resource Identifier) défini dans
le document RFC1945. Les applications serveur Web génèrent fréquemment un
contenu à partir de plusieurs sources ; le résultat final ne réside pas à un
emplacement précis, mais est créé si nécessaire. Les URI peuvent décrire des
ressources n’ayant pas d’emplacement défini.

29-2 Guide du développeur


Activité d’un serveur HTTP

En-tête de message de requête HTTP


Les messages de requête HTTP contiennent plusieurs en-têtes donnant des
informations sur le client, la cible de la requête, la manière dont la requête doit
être traitée et ce qui a été expédié avec la requête. Chaque en-tête est identifié
par un nom, comme “Host” suivi d’une valeur chaîne. Soit par exemple, la
requête HTTP suivante :
GET /art/gallery.dll/animals?animal=dog&color=black HTTP/1.0
Connection: Keep-Alive
User-Agent: Mozilla/3.0b4Gold (WinNT; I)
Host: www.TSite.com:1024
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
La première ligne identifie la requête comme un GET. Un message de requête
GET demande à l’application serveur Web de renvoyer le contenu associé à
l’URI suivant le mot GET (ici /art/gallery.dll/animals?animal=doc&color=black).
La dernière partie de la première ligne indique que le client utilise le standard
HTTP 1.0.
La deuxième ligne est l’en-tête Connection qui indique que la connexion ne doit
pas être fermée tant que la requête n’a pas été traitée. La troisième ligne est
l’en-tête User-Agent qui donne des informations sur le programme qui a généré
la demande. La ligne suivante définit l’en-tête Host et indique le nom et le port
de l’hôte sur le serveur qui est contacté pour constituer la connexion. La dernière
ligne est un en-tête Accept qui énumère les types de données que le client
accepte en réponse.

Activité d’un serveur HTTP


La nature client/serveur des navigateurs Web peut sembler, à tort, simple à
implémenter. Pour la majorité des utilisateurs, la récupération d’informations sur
le Web est une procédure ne nécessitant que quelques clics de souris. Certains
utilisateurs ont quelques notions de la syntaxe HTML et de la nature client/
serveur des protocoles utilisés. Ceci est généralement suffisant pour la création
de sites Web simples en mode page. Les créateurs de pages Web plus complexes
ont accès à de nombreuses options pour automatiser la recherche et la
présentation des informations au format HTML.
Avant de construire une application serveur Web, il est utile de comprendre
comment le client effectue une requête et comment le serveur y répond.

Composition des requêtes client


Lorsqu’un lien hypertexte HTML est sélectionné (ou lorsque l’utilisateur indique
un URL), le navigateur lit les informations sur, entre autres, le protocole, le
domaine spécifié, le chemin d’accès aux informations, la date et l’heure,
l’environnement système et le navigateur lui-même. Il compose ensuite une
requête.

Création d’applications serveur pour Internet 29-3


Activité d’un serveur HTTP

Par exemple, pour afficher une page d’images basée sur des critères représentés
par des boutons à l’écran, le client peut générer cet URL :
http://www.TSite.com/art/gallery.dll/animals?animal=dog&color=black
qui décrit un serveur HTTP dans le domaine www.TSite.com. Le client contacte
www.TSite.com, se connecte au serveur HTTP et lui transmet une requête. La
requête est semblable à celle-ci :
GET /art/gallery.dll/animals?animal=dog&color=black HTTP/1.0
Connection: Keep-Alive
User-Agent: Mozilla/3.0b4Gold (WinNT; I)
Host: www.TSite.com:1024
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*

Traitement des requêtes client par le serveur


Le serveur Web reçoit une requête du client et effectue diverses opérations en
fonction de sa configuration. Si le serveur reconnaît la partie /gallery.dll de la
requête comme un programme, il transfère à ce programme des informations sur
la requête. La façon dont les informations sur la requête sont passées au
programme dépend du type d’application serveur Web :
• Si le programme est un programme CGI (Common Gateway Interface), le
serveur passe directement les informations contenues dans la requête au
programme CGI. Le serveur attend l’exécution du programme. Lorsque le
programme CGI se termine, il retransmet le contenu au serveur.
• Si le programme est de type WinCGI, le serveur ouvre un fichier et y écrit les
informations de requête. Il exécute ensuite le programme Win-CGI en lui
transmettant l’emplacement du fichier contenant la description de la demande
du client et l’emplacement du fichier que le programme Win-CGI doit utiliser
pour créer le contenu. Le serveur attend l’exécution du programme. Lorsque
le programme se termine, le serveur lit le fichier de contenu créé par le
programme Win-CGI.
• Si le programme est une DLL, le serveur la charge si nécessaire et passe les
informations contenues dans la requête à la DLL, sous forme de structure. Le
serveur attend l’exécution du programme. Lorsque la DLL se referme, elle
retransmet le contenu au serveur.
Dans ces trois cas, le programme agit sur la requête et effectue les actions
demandées par le programmeur : accéder aux bases de données, faire des
recherches ou des calculs simples, créer ou sélectionner des documents HTML,
etc.

Réponses aux requêtes client


Lorsqu’une application serveur Web a fini de traiter une requête client, il
construit une page de code HTML (ou autre contenu MIME) et la transmet au
client (via le serveur) qui l’affiche.

29-4 Guide du développeur


Applications serveur Web

La méthode retenue pour l’envoi de la réponse dépend aussi du type du


programme :
• Lorsqu’un script Win-CGI se termine, il construit une page HTML, la place
dans un fichier, stocke les informations de réponse dans un autre fichier et
transmet l’emplacement de ces deux fichiers au serveur. Le serveur ouvre
ensuite ces fichiers et envoie la page HTML au client.
• Lorsqu’une DLL se termine, elle passe la page HTML et les informations de
réponse au serveur, qui les transmet au client.
Le fait de créer une application serveur Web en tant que DLL réduit la charge
de travail du système en diminuant le nombre de processus et d’accès disque
requis pour répondre à une requête.

Applications serveur Web


Les applications serveur Web étendent les capacités des serveurs Web existants.
L’application serveur Web reçoit les messages de requête HTTP du serveur Web,
effectue les opérations demandées dans ces messages et transmet ses réponses au
serveur Web. Toute opération que vous pouvez effectuer avec une application
Delphi peut être incorporée dans une application serveur Web.

Types d’applications serveur Web


En utilisant les composants internet, vous pouvez créer quatre types
d’applications serveur Web. Chaque type utilise un descendant spécifique
TWebApplication, TWebRequest et TWebResponse.

Tableau 29.1 Composants des applications serveur Web


Objet
Type d’application application Objet requête Objet réponse
DLL Microsoft Server (ISAPI) TISAPIApplication TISAPIRequest TISAPIResponse
DLL Netscape Server (NSAPI) TISAPIApplication TISAPIRequest TISAPIResponse
Application Console CGI TCGIApplication TCGIRequest TCGIResponse
Application Windows CGI TCGIApplication TWinCGIRequest TWinCGIResponse

ISAPI et NSAPI
Une application serveur Web ISAPI ou NSAPI est une DLL qui est chargée par
le serveur Web. Les informations de requête client sont transmises à la DLL sous
forme de structure et évaluées par les objets TISAPIApplication, qui créent les
objets TISAPIRequest et TISAPIResponse. Chaque message de requête est
automatiquement traité dans un thread distinct.

Création d’applications serveur pour Internet 29-5


Applications serveur Web

CGI autonome
Une application serveur Web CGI autonome est une application console qui
reçoit les informations de requête client sur l’entrée standard et transmet les
résultats au serveur sur la sortie standard. Les données sont évaluées par l’objet
TCGIApplication, qui crée le répartiteur, et par les objets TCGIRequest et
TCGIResponse. Chaque message de requête est traité dans une instance distincte
de l’application.

Win-CGI autonome
Une application serveur Web Win-CGI autonome est une application Windows
qui reçoit les informations de requête client depuis un fichier de configuration
(INI) créé par le serveur et écrit les résultats dans un fichier que le serveur
transmet au client. Le fichier INI est évalué par l’objet TCGIApplication, qui crée
le répartiteur, et par les objets TWinCGIRequest et TWinCGIResponse. Chaque
message de requête est traité dans une instance distincte de l’application.

Création d’applications serveur Web


Pour créer une application serveur Web, sélectionnez Fichier|Nouveau dans la
fenêtre principale, puis cliquez sur l’icône Application serveur Web dans la boîte
de dialogue Nouveaux éléments. Une boîte de dialogue apparaît, dans laquelle
vous pouvez sélectionner un type d’application serveur Web :
• ISAPI et NSAPI : si vous sélectionnez ce type d’application, le projet est
configuré comme une DLL avec les méthodes exportées attendues par le
serveur Web. Il ajoute l'en-tête de la bibliothèque dans le fichier projet et les
entrées requises à la liste uses ainsi que la clause exports du fichier projet.
• Exécutable autonome CGI : si vous sélectionnez ce type d’application, le projet
est configuré comme une application en mode console et ajoute les entrées
requises à la clause uses du fichier projet.
• Exécutable autonome Win-CGI : si vous sélectionnez ce type d’application, le
projet est configuré comme une application Windows et ajoute les entrées
requises à la clause uses du fichier projet.
Choisissez le type d’application serveur Web qui communique avec le type de
serveur Web que votre application utilisera. Ceci crée un nouveau projet
configuré pour utiliser les composants Internet et contenant un module Web
vide.

Le module Web
Le module Web (TWebModule) est un descendant de TDataModule ; il s’utilise de
la même façon pour offrir un contrôle centralisé pour les règles de gestion et les
composants non visuels dans l’application Web.

29-6 Guide du développeur


Applications serveur Web

Ajoutez tout générateur de contenu que votre application utilise pour générer les
messages de réponse. Il peut s’agir de générateurs de contenu intégrés, tels que
TPageProducer, TDataSetPageProducer, TDataSetTableProducer, TQueryTableProducer
et TMIDASPageProducer ou de descendants de TCustomContentProducer que vous
avez créés vous-même. Si votre application génère des messages de réponse
incluant des données extraites d’une base de données, vous pouvez ajouter des
composants d’accès aux données ou des composants particuliers pour écrire un
serveur Web qui fait office de client dans une application MIDAS.
Le module Web ne se contente pas de stocker les composants non visuels et des
règles de gestion : il fait aussi office de répartiteur en associant les messages de
requête HTTP reçus aux éléments d’action qui génèrent les réponses à ces
requêtes.
Vous avez peut-être déjà un module de données paramétré avec les composants
non visuels et les règles de gestion que vous souhaitez utiliser dans votre
application Web. Vous pouvez alors remplacer le module Web par ce module de
données : il suffit de supprimer le module Web généré automatiquement et de le
remplacer par votre module de données. Ajoutez ensuite un composant
TWebDispatcher à votre module de données pour qu’il puisse répartir les
messages de requête vers les éléments d’action, comme le ferait un module Web.
Si vous voulez modifier la façon dont les éléments d’action sont choisis pour
répondre aux messages de requête HTTP reçus, dérivez un nouveau composant
répartiteur de TCustomWebDispatcher et ajoutez-le au module de données.
Votre projet ne peut contenir qu’un répartiteur. Il peut s’agir soit du module
Web qui est automatiquement généré lorsque vous créez le projet, soit du
composant TWebDispatcher que vous ajoutez au module de données qui remplace
le module Web. Si un second module de données contenant un répartiteur est
créé lors de l’exécution, l’application serveur Web générera une erreur.
Remarque Le module Web que vous paramétrez en phase de conception est en fait un
modèle. Dans les applications ISAPI et NSAPI, chaque message de requête crée
un thread distinct. Une instance du module Web et de son contenu est créée
dynamiquement pour chaque thread.
Attention Le module Web d’une application serveur Web à base de DLL est mis en
mémoire cache pour une utilisation ultérieure afin d’améliorer les temps de
réponse. L’état du répartiteur et sa liste d’actions ne sont pas réinitialisés entre
deux requêtes. Si vous activez ou désactivez des éléments d’action en cours
d’exécution, vous obtiendrez des résultats imprévisibles lorsque ce module sera
utilisé pour les requêtes client suivantes.

L’objet application Web


Le projet préparé pour votre application Web contient une variable globale du
nom de Application. Application est un descendant de TWebApplication (soit
TISAPIApplication, soit TCGIApplication) approprié pour le type d’application que
vous créez. Il s’exécute en réponse aux messages de requête HTTP reçus par le
serveur Web.

Création d’applications serveur pour Internet 29-7


Structure d’une application serveur Web

Attention N’incluez pas l’unité forms dans la clause uses du projet après l’unité CGIApp
ou ISAPIApp. Forms déclare en effet une autre variable globale du nom
d’Application et, si elle apparaît après l’unité CGIApp ou ISAPIApp, Application
sera initialisée comme un objet de type erroné.

Structure d’une application serveur Web


Lorsque l’application Web reçoit un message de requête HTTP, elle crée un objet
TWebRequest pour représenter le message de requête HTTP et un objet
TWebResponse pour représenter la réponse qui doit être retournée. L’application
transmet ensuite ces objets au répartiteur Web (le module Web ou un composant
TWebDispatcher).
Le répartiteur Web contrôle le déroulement de l’application serveur Web. Il gère
un ensemble d’éléments d’action (TWebActionItem) qui sait comment réagir à
certains messages de requête HTTP. Le répartiteur identifie les éléments d’action
ou les composants à répartition automatique aptes à répondre au message de
requête HTTP et transmet les objets requête et réponse au gestionnaire identifié
pour qu’il lance les opérations demandées ou formule un message de réponse.
Le répartiteur est décrit plus en détail dans la section “Le répartiteur Web” à la
page 29-9.
Figure 29.2 Structure d’une application serveur

Les éléments d’action gèrent la lecture de la requête et l’assemblage d’un


message de réponseLes composants générateur de contenu spécialisés aident les
éléments d’action à générer dynamiquement le contenu des réponses de message
pouvant inclure du code HTML personnalisé ou un autre contenu MIME. Les
générateurs de contenu se servent d’autres générateurs de contenu ou
descendants de THTMLTagAttributes, pour créer le contenu du message de
réponse. Pour plus d’informations sur les générateurs de contenu, voir
“Génération du contenu des messages de réponse” à la page 29-18.

29-8 Guide du développeur


Le répartiteur Web

Si vous créez le client Web dans une application de base de données


multiniveau, votre application serveur Web peut inclure d'autres composants à
répartition automatique qui représentent des informations de base de données
codées en XML et des classes de manipulation de base de données codées en
javascript. Le répartiteur fait appel à ces composants à répartition automatique
pour traiter le message de requête après avoir essayé tous ses éléments d'action.
Lorsque tous les éléments d’action (ou composants à répartition automatique) ont
créé leur réponse en remplissant l’objet TWebResponse, le répartiteur transmet le
résultat à l’application Web qui le transmet en retour au client via le serveur
Web.

Le répartiteur Web
Si vous utilisez un module Web, il agit comme un répartiteur Web. Si vous
utilisez un module de données existant, vous devez lui ajouter un composant
(TWebDispatcher). Le répartiteur gère un ensemble d’éléments d’action qui savent
comment répondre à certains types de messages de requête. Lorsque l’application
Web transmet un objet requête et un objet réponse au répartiteur, il choisit un
ou plusieurs éléments d’action pour répondre à la requête.

Ajout d’actions au répartiteur


Ouvrez l’éditeur d’action depuis l’inspecteur d’objets en cliquant sur les trois
points de la propriété Actions du répartiteur. Il est possible d’ajouter des
éléments d’action au répartiteur en cliquant sur le bouton Ajouter dans l’éditeur
d’action.
Ajoutez des actions au répartiteur pour répondre aux différentes méthodes de
requête ou URI de destination. Vous pouvez paramétrer vos éléments d’action de
diverses façons. Vous pouvez commencer par les éléments d’action qui font un
prétraitement des requêtes et terminer par une action par défaut qui vérifie que
la réponse est complète et l’envoie ou retourne un code d’erreur. Vous pouvez
aussi ajouter un élément d’action pour chaque type de requête, auquel cas
chaque élément d’action traitera entièrement la requête.
Les éléments d’action sont décrits en détail à la section “Eléments d’action” à la
page 29-10.

Répartition des messages de requête


Lorsque le répartiteur reçoit la requête client, il génère un événement
BeforeDispatch. Ceci permet à votre application de faire un prétraitement du
message de requête avant qu’il ne soit vu par les éléments d’action.

Création d’applications serveur pour Internet 29-9


Eléments d’action

Ensuite, le répartiteur recherche dans sa liste d’éléments d’action un élément qui


corresponde à la partie chemin de l’URL de destination du message de requête
et sachant fournir le service spécifié comme méthode du message de requête.
Pour ce faire, il compare les propriétés PathInfo et MethodType de l’objet
TWebRequest avec ces mêmes propriétés dans l’élément d’action.
Lorsque le répartiteur trouve l’élément d’action recherché, il déclenche cet
élément d’action, qui effectue l’une des opérations suivantes :
• Il fournit le contenu de la réponse et envoie la réponse ou signale que la
requête est complètement traitée.
• Il ajoute des informations à la réponse puis permet à d’autres éléments
d’action de finir le travail.
• Il transmet la requête à d’autres éléments d’action.
Après avoir vérifié tous ses éléments d’action, si le message n’est pas traité, le
répartiteur vérifie tous les composants à répartition automatique spécialement
recensés n’utilisant pas d’élément d’action. Ces composants sont propres aux
applications de bases de données multiniveaux, décrites dans la section
“Construction des applications Web avec InternetExpress” à la page 14-34
Si, après contrôle de tous les éléments d’action et des composants à répartition
automatique spécialement recensés, le message de requête n’est pas traité, le
répartiteur appelle l’élément d’action par défaut. Il n’est pas nécessaire que cet
élément d’action corresponde à l’URL de destination ou à la méthode de requête.
Si le répartiteur atteint la fin de la liste d’actions (y compris l’éventuelle action
par défaut) et qu’aucune action n’a été déclenchée, rien n’est retourné au
serveur. Le serveur met alors fin à la connexion avec le client.
Si la requête est traitée par les éléments d’action, le répartiteur génère un
événement AfterDispatch. Ceci représente la dernière possibilité pour votre
application de vérifier que la réponse a été générée et de faire les modifications
requises.

Eléments d’action
Chaque élément d’action (TWebActionItem) effectue une tâche précise en réponse
à un type spécifique de message de requête.
Les éléments d’action peuvent répondre entièrement à une requête ou n’y
répondre que partiellement et laisser d’autres éléments d’action finir le travail.
Les éléments d’action peuvent envoyer le message de réponse HTTP pour la
requête, ou simplement préparer une partie de la réponse, que d’autres éléments
d’action termineront. Si une réponse est complétée par les éléments d’action mais
non envoyée, l’application serveur Web envoie le message de réponse.

29-10 Guide du développeur


Eléments d’action

Choix du déclenchement des éléments d’action


La plupart des propriétés d’un élément d’action indiquent le moment auquel le
répartiteur doit le sélectionner pour gérer un message de requête HTTP. Pour
définir les propriétés d’un élément d’action, vous devez d’abord ouvrir l’éditeur
d’action : sélectionnez la propriété Actions du répartiteur dans l’inspecteur
d’objets et cliquez sur les trois points. Lorsqu’une action est sélectionnée dans
l’éditeur, ses propriétés peuvent être modifiées dans l’inspecteur d’objets.

URL de destination
Le répartiteur compare la valeur de la propriété PathInfo d’un élément d’action à
celle de la propriété PathInfo du message de requête. La valeur de cette propriété
doit être le chemin d’accès à l’URL pour toutes les requêtes que l’élément
d’action est prêt à gérer. Par exemple dans cet URL
http://www.TSite.com/art/gallery.dll/mammals?animal=dog&color=black
si la partie /gallery.dll indique l’application serveur Web, le chemin d’accès est
/mammals
Utilisez les informations de chemin d’accès pour indiquer où votre application
Web doit rechercher des informations au moment de traiter des requêtes ou pour
subdiviser votre serveur Web en sous-services logiques.

Type de méthode de requête


La propriété MethodType d’un élément d’action indique quels types de messages
de requête il peut traiter. Le répartiteur compare la propriété MethodType d’un
élément d’action à la propriété MethodType du message de requête. MethodType
peut avoir les valeurs suivantes :

Tableau 29.2 Valeurs de MethodType


Valeur Signification
mtGet La requête demande que les informations associées à l’URI de destination
soient retournées dans un message de réponse.
mtHead La requête demande les propriétés d’en-tête d’une réponse, comme dans le cas
du traitement d’une requête mtGet, mais omet le contenu de la réponse.
mtPost La requête fournit les informations à émettre dans l’application Web.
mtPut La requête demande que la ressource associée à l'URI de destination soit
remplacée par le contenu du message de requête.
mtAny L’élément d’action correspond à tout type de méthode de requête, y compris
mtGet, mtHead, mtPut et mtPost.

Activation et désactivation des éléments d’action


Chaque élément d’action possède une propriété Enabled qui permet de l’activer et
de le désactiver. En mettant Enabled à False, vous désactivez l’élément d’action,
qui n’est plus pris en compte par le répartiteur lors de la recherche d’un élément
d’action capable de gérer une requête.

Création d’applications serveur pour Internet 29-11


Eléments d’action

Un gestionnaire d’événement BeforeDispatch peut définir quels éléments d’action


doivent traiter une requête en modifiant la propriété Enabled des éléments
d’action avant que le répartiteur ne commence sa recherche.
Attention Si vous modifiez la propriété Enabled d’une action lors de l’exécution, vous
risquez d’obtenir des résultats imprévisibles pour les requêtes suivantes. Si
l’application serveur Web est une DLL qui met en cache les modules Web, l’état
initial ne sera pas réinitialisé pour la requête suivante. Utilisez l’événement
BeforeDispatch pour vous assurer que tous les éléments d’action sont correctement
initialisés.

Choix d’un élément d’action par défaut


Un seul élément d’action peut être l’élément d’action par défaut. L’élément
d’action par défaut est choisi en mettant sa propriété Default à True. Lorsque vous
mettez la propriété Default d’un élément d’action à True, la propriété Default du
précédent élément d’action par défaut (s’il en existait un) passe à False.
Lorsque le répartiteur recherche dans sa liste d’éléments d’action celui qui peut
gérer la requête, il conserve en mémoire le nom de l’élément d’action par défaut.
Si la requête n’a pas été entièrement honorée lorsque le répartiteur atteint la fin
de la liste d’éléments d’action, il exécute l’élément d’action par défaut.
Le répartiteur ne vérifie pas les propriétés PathInfo et MethodType de l’élément
d’action par défaut. Il ne vérifie pas non plus la propriété Enabled de l’élément
d’action par défaut. Vous pouvez ainsi vous assurer que l’élément d’action par
défaut n’est appelé qu’en dernier recours en mettant sa propriété Enabled à False.
L’élément d’action par défaut doit être prêt à gérer toute requête détectée, même
si ce n’est qu’en retournant un code d’erreur signalant un URI ou une valeur de
propriété MethodType non valide. Si l’élément d’action par défaut ne peut pas
traiter la requête, aucune réponse n’est transmise au client Web.
Attention Si vous modifiez la propriété Default d’une action lors de l’exécution, vous
risquez d’obtenir des résultats imprévisibles pour la requête active. Si la
propriété Default d’une action ayant déjà été déclenchée passe à True, cette action
ne sera pas réévaluée et le répartiteur ne la déclenchera pas lorsqu’il atteindra la
fin de la liste d’actions.

Réponse aux messages de requête avec des éléments


d’action
Le plus gros du travail de l’application serveur Web est effectué par les éléments
d’action lors de leur exécution. Lorsque le répartiteur Web déclenche un élément
d’action, ce dernier peut répondre au message de requête en cours de deux
manières :
• Si un composant générateur est associé à l’élément d’action comme valeur de sa
propriété Producer, ce générateur affecte automatiquement le contenu (Content) du
message de réponse à l’aide de sa méthode Content. La page Internet de la palette
de composants inclut une série de composants générateur de contenu qui
permettent d’élaborer une page HTML pour le contenu du message de réponse.

29-12 Guide du développeur


Eléments d’action

• Une fois que le générateur a affecté un contenu de réponse (si un générateur


est associé), l’élément d’action reçoit un événement OnAction. Le gestionnaire
d’événement OnAction reçoit l’objet TWebRequest qui représente le message de
requête HTTP et un objet TWebResponse permettant de remplir les
informations de réponse.
Si le contenu de l’élément d’action peut être généré par un seul générateur de
contenu, il est plus simple d’affecter le générateur de contenu comme valeur de
la propriété Producer de l’élément d’action. Toutefois, tout générateur de contenu
demeure accessible à partir du gestionnaire d’événement OnAction. Le
gestionnaire d’événement OnAction offre une souplesse supplémentaire, qui vous
permet d’utiliser plusieurs générateurs de contenu, d’affecter des propriétés de
message de réponse, etc.
Le composant générateur de contenu et le gestionnaire d’événement OnAction
peuvent utiliser tous les objets et toutes les méthodes de bibliothèque d’exécution
pour répondre aux messages de requête. Ils peuvent accéder à des bases de
données, faire des calculs, construire ou sélectionner des documents HTML, etc.
Pour plus d’informations sur la génération de contenu de réponse à l’aide de
composants générateur de contenu, voir “Génération du contenu des messages
de réponse” à la page 29-18.

Envoi de la réponse
Un gestionnaire d’événement OnAction peut renvoyer la réponse au client Web à
l’aide des méthodes de l’objet TWebResponse. Cependant, si aucun élément
d’action n’envoie la réponse au client, elle sera envoyée par l’application serveur
Web si le dernier élément d’action ayant analysé la requête indique que la
requête a été traitée.

Utilisation de plusieurs éléments d’action


Vous pouvez répondre à une requête depuis un seul élément d’action ou répartir
le travail entre plusieurs éléments d’action. Si l’élément d’action n’élabore pas
complètement le message de réponse, il doit indiquer cet état dans le
gestionnaire d’évément OnAction en mettant le paramètre Handled à False.
Si de nombreux éléments d’action se partagent la tâche de répondre aux
messages de requête et que chacun met Handled à False, pensez à vérifier que
l’élément d’action par défaut laisse le paramètre Handled à True, faute de quoi
aucune réponse ne serait envoyée au client Web.
Lorsque plusieurs éléments d’action se partagent la gestion du message de
requête, il faut que le gestionnaire d’événement OnAction de l’élément d’action
par défaut ou que le gestionnaire d’événement AfterDispatch du répartiteur
vérifie que toutes les tâches ont bien été effectuées. Si ce n’est pas le cas, il devra
définir le code d’erreur adéquat.

Création d’applications serveur pour Internet 29-13


Accès aux informations de requêtes client

Accès aux informations de requêtes client


Lorsqu’un message de requête HTTP est reçu par l’application serveur Web, les
en-têtes de la requête client sont chargés dans les propriétés d’un objet
TWebRequest. Dans les applications NSAPI et ISAPI, le message de requête est
encapsulé par un objet TISAPIRequest. Les applications Console CGI utilisent les
objets TCGIRequest, alors que les applications Windows CGI utilisent les objets
TWinCGIRequest.
Les propriétés de l’objet requête sont en lecture seule. Vous pouvez les utiliser
pour recueillir toutes les informations disponibles dans la requête client.

Propriétés contenant des informations d’en-tête de


requête
La plupart des propriétés d’un objet requête contiennent des informations sur la
requête provenant de l’en-tête de requête HTTP. Toutes les requêtes, cependant,
ne fournissent pas une valeur pour toutes les propriétés. De plus, certaines
requêtes peuvent inclure des champs d’en-tête qui n’apparaissent pas dans une
propriété de l’objet requête, car le standard HTTP est en évolution permanente.
Pour obtenir la valeur d’un champ d’en-tête de requête qui n’apparaît pas
comme l’une des propriétés de l’objet requête, utilisez la méthode
GetFieldByName.

Propriétés identifiant la destination


La destination complète du message de requête est fournie par la propriété URL.
En règle générale, il s’agit d’un URL qui peut se décomposer en un protocole
(HTTP), un Host (système serveur), un ScriptName (application serveur), un
PathInfo (emplacement sur l’hôte) et un Query.
Chacune de ces parties est reflétée dans sa propre propriété. Le protocole est
toujours HTTP, Host et ScriptName identifient l’application serveur Web. Le
répartiteur utilise la partie PathInfo lorsqu’il associe des éléments d’action aux
messages de requête. La partie Query est utilisée par certaines requêtes pour
spécifier des détails sur les informations demandées. Sa valeur est elle aussi
analysée pour vous dans la propriété QueryFields.

Propriétés décrivant le client Web


La requête inclut en outre plusieurs propriétés qui fournissent des informations
sur son origine. Il peut s’agir de l’adresse e-mail de l’expéditeur (propriété From)
ou de l’URI d’origine du message (propriétés Referer ou RemoteHost). Si la
requête a un contenu et que ce contenu ne provient pas du même URI que la
requête, la source de ce contenu est indiquée par la propriété DerivedFrom. Vous
pouvez également connaître l’adresse IP du client (propriété RemoteAddr) et le
nom et la version de l’application ayant envoyé la requête (propriété UserAgent).

29-14 Guide du développeur


Accès aux informations de requêtes client

Propriétés identifiant le but de la requête


La propriété Method est une chaîne décrivant ce que le message de requête
demande à l’application serveur. Le standard HTTP 1.1 définit les méthodes
suivantes :

Valeur Informations demandées


OPTIONS Informations sur les options de communication disponibles.
GET Informations identifiées par la propriété URL.
HEAD Informations d’en-tête issues d’un message GET équivalent, sans le contenu
de la réponse.
POST L’application serveur doit émettre les données incluses dans la propriété
Content, de la façon appropriée.
PUT L’application serveur doit remplacer la ressource indiquée par la propriété
URL par les données de la propriété Content.
DELETE L’application serveur doit supprimer ou masquer la ressource identifiée par
la propriété URL.
TRACE L’application serveur doit confirmer la réception de la requête.

La propriété Method peut indiquer toute autre méthode que le client Web
demande au serveur.
Il n’est pas nécessaire que l’application serveur Web fournisse une réponse pour
toutes les valeurs possibles de la propriété Method. Le standard HTTP exige
cependant qu’elle sache répondre aux requêtes GET et HEAD.
La propriété MethodType indique si la valeur de Method est GET (mtGet), HEAD
(mtHead), POST (mtPost), PUT (mtPut) ou une autre chaîne (mtAny). Le
répartiteur fait correspondre la valeur de la propriété MethodType avec celle de la
propriété MethodType de tous les éléments d’action.

Propriétés décrivant la réponse attendue


La propriété Accept indique les types de support que le client Web accepte
comme contenu du message de réponse. La propriété IfModifiedSince indique si le
client ne souhaite que les informations modifiées récemment. La propriété Cookie
inclut des informations d’état (généralement ajoutées par l’application) qui
peuvent modifier la réponse.

Propriétés décrivant le contenu


La plupart des requêtes n’incluent aucun contenu car elles ne font que demander
des informations. Certaines requêtes, cependant, telles que les requêtes POST,
fournissent un contenu que l’application serveur Web doit utiliser. Le type de
support du contenu est précisé dans la propriété ContentType et sa longueur l’est
dans la propriété ContentLength. Si le contenu du message a été codé (par
compression des données, par exemple), cette information figure dans la
propriété ContentEncoding. Le nom et le numéro de version de l’application ayant
généré le contenu sont indiqués dans la propriété ContentVersion. La propriété
Title fournit elle aussi, parfois, des renseignements sur le contenu.

Création d’applications serveur pour Internet 29-15


Création de messages de réponse HTTP

Contenu d’un message de requête HTTP


En plus des champs d’en-tête, certains messages de requête incluent une partie
contenu que l’application serveur Web doit traiter. Par exemple, une requête
POST peut comporter des informations qui doivent être ajoutées à une base de
données gérée par l’application serveur Web.
La valeur non traitée du contenu est fournie par la propriété Content. Si le
contenu peut être réparti dans des champs séparés par le caractère “&”, cette
version sera disponible dans la propriété ContentFields.

Création de messages de réponse HTTP


Lorsque l’application serveur Web crée un objet TWebRequest pour un message
de requête HTTP reçu, elle crée aussi un objet TWebResponse correspondant pour
représenter le message de réponse qui sera envoyé en retour. Dans les
applications NSAPI et ISAPI, le message de réponse est encapsulé par un objet
TISAPIResponse. Les applications GCI Console utilisent les objets TCGIResponse et
les applications Windows CGI utilisent les objets TWinCGIResponse.
Les éléments d’action qui génèrent la réponse à la requête client renseignent les
propriétés de l’objet réponse. Dans certains cas, il suffit de retourner un code
d’erreur ou de passer la requête à une autre URI. Mais dans d’autres cas, il faut
parfois effectuer des calculs tels que l’élément d’action doit aller chercher des
informations à d’autres sources puis les regrouper avant de les présenter au
format final. La plupart des messages de requête nécessitent une réponse, même
s’il ne s’agit que d’indiquer au client que l’opération demandée a été effectuée.

Informations d’en-tête de réponse


La plupart des propriétés de l’objet TWebResponse représentent les informations
d’en-tête du message de réponse HTTP retourné au client Web. Un élément
d’action définit ces propriétés à partir de son gestionnaire d’événement OnAction.
Il n’est pas nécessaire que tous les messages de réponse contiennent une valeur
pour toutes les propriétés de l’en-tête. Les propriétés qui doivent être renseignées
dépendent de la nature de la requête et du statut de la réponse.

Indication du statut de la réponse


Tout message de réponse doit inclure un code indiquant le statut de la réponse.
Vous pouvez spécifier ce code en définissant la propriété StatusCode. Le standard
HTTP définit des codes de statut à la signification prédéfinie. De plus, vous
pouvez définir vos propres codes de statut avec les valeurs possibles inutilisées.
Un code de statut est un numéro à trois chiffres dans lequel le chiffre le plus
significatif indique la classe de la réponse, de la façon suivante :
• 1xx : Information (la requête a été reçue mais n’a pas été entièrement traitée).

29-16 Guide du développeur


Création de messages de réponse HTTP

• 2xx : Succès (la requête a été reçue, comprise et acceptée).


• 3xx : Redirection (le client doit intervenir pour compléter la requête).
• 4xx : Erreur du client (la requête est incompréhensible ou ne peut être traitée).
• 5xx : Erreur du serveur (la requête est valide mais le serveur n’a pas pu la
traiter).
Une chaîne est associée à chaque code de statut ; elle donne la signification de ce
code de statut. Elle se trouve dans la propriété ReasonString. Pour les codes de
statut prédéfinis, il n’est pas nécessaire de définir la propriété ReasonString. Si
vous créez vos propres codes de statut, cependant, pensez à définir la propriété
ReasonString.

Indication d’attente d’une action du client


Lorsque le code de statut est compris entre 300 et 399, le client doit lancer une
action pour que l’application serveur Web puisse traiter la requête en entier. Si
vous devez rediriger le client vers un autre URI, ou indiquer qu’un nouvel URI
a été créé pour traiter cette requête, utilisez la propriété Location. Si le client doit
fournir un mot de passe pour poursuivre, définissez la propriété
WWWAuthenticate.

Description de l’application serveur


Certaines propriétés d’en-tête de réponse décrivent les capacités de l’application
serveur Web. La propriété Allow indique les méthodes auxquelles elle peut
répondre. La propriété Server contient le nom et la version de l’application
servant à générer la réponse. La propriété Cookies peut contenir des informations
d’état concernant l’utilisation par le client de l’application serveur qui sont
incluses dans les messages de requête ultérieurs.

Description du contenu
Plusieurs propriétés décrivent le contenu de la réponse. ContentType fournit le
type de support de la réponse, ContentVersion le numéro de version de ce
support. ContentLength indique la longueur de la réponse. Si le contenu est codé
(par compression des données, par exemple), indiquez-le dans la propriété
ContentEncoding. Si le contenu provient d’un autre URI, indiquez-le dans la
propriété DerivedFrom. Si la valeur du contenu tient compte de la date, utilisez
les propriétés LastModified et Expires pour indiquer si le contenu est toujours
valide. La propriété Title peut fournir des informations descriptives sur le
contenu.

Définition du contenu de la réponse


Dans certains cas, la réponse au message de requête est entièrement contenue
dans les propriétés d’en-tête de la réponse. La plupart du temps, cependant, un
élément d’action assigne un contenu au message de réponse. Ce contenu peut
être des informations statiques stockées dans un fichier ou des informations
générées par l’élément d’action ou son générateur de contenu.

Création d’applications serveur pour Internet 29-17


Génération du contenu des messages de réponse

Vous pouvez définir le contenu du message de réponse à l’aide des propriétés


Content et ContentStream.
La propriété Content est une chaîne. Les chaînes Delphi ne sont pas limitées à
des valeurs littérales, aussi la valeur de la propriété Content peut-elle être une
série de commandes HTML, un contenu graphique ou tout type de contenu
MIME.
Utilisez la propriété ContentStream si le contenu du message de réponse peut être
lu dans un flux. Par exemple, si le message de réponse doit envoyer le contenu
d’un fichier, utilisez un objet TFileStream pour la propriété ContentStream. Comme
avec la propriété Content, ContentStream peut fournir une chaîne de commandes
HTML ou un autre contenu de type MIME. Si vous utilisez la propriété
ContentStream, ne libérez pas le flux vous-même : l’objet réponse Web le libérera
automatiquement.
Remarque Si la valeur de la propriété ContentStream n’est pas nil, la propriété Content est
ignorée.

Envoi de la réponse
Si vous êtes sûr que le traitement du message de requête est terminé, vous
pouvez envoyer une réponse directement depuis le gestionnaire d’événement
OnAction. L’objet réponse offre deux méthodes d’envoi de réponses :
SendResponse et SendRedirect. Appelez SendResponse pour envoyer une réponse
avec le contenu et les propriétés d’en-tête de l’objet TWebResponse. Si votre action
doit se limiter à orienter le client Web vers un autre URI, utilisez la méthode
SendRedirect, plus efficace.
Si aucun des gestionnaires d’événements n’envoie de réponse, l’application Web
l’envoie lorsque le répartiteur se referme. Cependant, si aucun élément d’action
n’indique qu’il a traité la réponse, l’application ferme la connexion au client Web
sans envoyer de réponse.

Génération du contenu des messages de réponse


Delphi met à votre disposition plusieurs objets qui aideront vos éléments
d’action à générer un contenu pour les messages de réponse HTTP. Vous pouvez
utiliser ces objets pour générer des chaînes de commandes HTML enregistrées
dans un fichier ou transmises directement au client Web. Vous pouvez créer vos
propres générateurs de contenu en les dérivant de TCustomContentProducer ou de
l’un de ses descendants.
TCustomContentProducer offre une interface générique pour la création de
contenus de type MIME dans un message de réponse HTTP. Ses descendants
incluent des générateurs de page et des générateurs de tableau :
• Les générateurs de page peuvent rechercher dans les documents HTML des
balises spéciales qu’ils remplacent par du code HTML spécifique. Ils sont
décrits dans la section suivante.

29-18 Guide du développeur


Génération du contenu des messages de réponse

• Les générateurs de tableau créent des commandes HTML à partir des


informations contenues dans un ensemble de données. Ils sont décrits dans
“Utilisation des bases de données dans les réponses” à la page 29-23.

Utilisation du composant générateur de page


Les générateurs de page (TPageProducer et des descendants) convertissent un
modèle HTML en remplaçant les balises HTML transparentes par du code
HTML personnalisé. Vous pouvez garder sous la main un jeu de modèles de
réponses qui seront remplis par les générateurs de page lorsque vous devrez
répondre à un message de requête HTTP. Vous pouvez chaîner des générateurs
de page pour construire de façon itérative un document HTML par traitements
successifs des balises HTML transparentes.

Modèles HTML
Un modèle HTML est une suite de commandes HTML et de balises HTML
transparentes. Une balise HTML transparente est au format :
<#Nom_de_balise Param1=Valeur1 Param2=Valeur2 ...>
Les crochets (< et >) définissent la portée de la balise. Le signe “#” vient
immédiatement après le crochet ouvrant, sans espace entre les deux. Il indique
au générateur de page que la chaîne qui suit est une balise HTML transparente.
Le nom de la balise suit immédiatement le signe dièse, sans espace entre les
deux. Le nom de la balise peut être tout identificateur valide ; il identifie le type
de conversion représenté par la balise.
Après un nom de balise, une balise HTML transparente peut parfois comporter
des paramètres fournissant des informations sur la conversion à effectuer.
Chaque paramètre est au format Paramètre=Valeur. Aucun espace ne doit figurer
entre le nom du paramètre, le signe égal et la valeur. Les paramètres sont
séparés par des espaces.
Les crochets (< et >) rendent la balise transparente pour les navigateurs HTML
qui ne reconnaissent pas la syntaxe “#Nom_de_balise”.
Bien que vous puissiez créer vos propres balises HTML transparentes pour
représenter tout type d'informations traitées par votre générateur de page,
C++Builder met à votre disposition plusieurs noms de balises associés à des
valeurs du type de données TTag. Ces noms de balise prédéfinis corrrespondent
aux commandes HTML susceptibles de varier d'un message de réponse à l'autre.
Ils sont décrits dans le tableau suivant :

Nom de la Valeur
balise de TTag La balise est convertie en :
Link tgLink Lien hypertexte. Le résultat est une séquence HTML
commençant par la balise <A> et se terminant par </A>.
Image tgImage Graphique. Le résultat est une balise HTML <IMG>.

Création d’applications serveur pour Internet 29-19


Génération du contenu des messages de réponse

Nom de la Valeur
balise de TTag La balise est convertie en :
Table tgTable Tableau HTML. Le résultat est une séquence HTML
commençant par la balise <TABLE> et se terminant par la
balise </TABLE>.
ImageMap tgImageMap Graphique contenant des zones sensibles. Le résultat est une
séquence HTML commençant par la balise <MAP> et se
terminant par </MAP>.
Object tgObject Objet ActiveX incorporé. Le résultat est une séquence HTML
commençant par la balise <OBJECT> et se terminant par la
balise </OBJECT>.
Embed tgEmbed DLL compatible Netscape. Le résultat est une séquence
HTML commençant par la balise <EMBED> et se terminant
par la balise </EMBED>.

Tout autre nom de balise est associé à tgCustom. Le générateur de page n’offre
aucun traitement spécifique des noms de balises prédéfinis. Ils sont simplement
fournis pour aider vos applications à structurer le processus de conversion des
tâches les plus fréquentes.
Remarque Les noms de balises prédéfinis tiennent compte de la différence entre majuscules
et minuscules.

Choix du modèle HTML


Les générateurs de page vous proposent plusieurs façons pour désigner le
modèle HTML. Vous pouvez donner à la propriété HTMLFile le nom du fichier
contenant le modèle HTML. Vous pouvez donner à la propriété HTMLDoc le
nom d’un objet TStrings contenant le modèle HTML. Si vous utilisez l’une de ces
propriétés pour désigner le modèle, vous pouvez générer les commandes HTML
converties en appelant la méthode Content.
Vous pouvez également appeler la méthode ContentFromString pour convertir
directement un modèle HTML composé d’une seule chaîne passée dans un
paramètre, ou appeler la méthode ContentFromStream pour lire le modèle HTML
depuis un flux. Ainsi, par exemple, vous pourriez placer vos modèles HTML
dans un champ mémo d’une base de données et, à l’aide de la méthode
ContentFromStream, obtenir les commandes HTML converties en lisant le modèle
depuis un objet TBlobStream.

Conversion des balises HTML transparentes


Le générateur de page convertit le modèle HTML lorsque vous appelez l’une de
ses méthodes Content. Lorsque la méthode Content détecte une balise HTML
transparente, elle déclenche un événement OnHTMLTag. Vous devez écrire un
gestionnaire d’événement pour définir le type de balise détecté et la remplacer
par un contenu personnalisé.
Si vous ne créez pas de gestionnaire d'événement OnHTMLTag pour le
générateur de page, les balises HTML transparentes sont remplacées par des
chaînes vides.

29-20 Guide du développeur


Génération du contenu des messages de réponse

Utilisation du générateur de page depuis un élément d’action


Un exemple d’utilisation d’un composant générateur de page est d’utiliser la
propriété HTMLFile pour spécifier un fichier contenant un modèle HTML. Le
gestionnaire d'événement OnAction appelle la méthode Content pour convertir le
modèle en séquence HTML finale :
procedure WebModule1.MyActionEventHandler(Sender: TObject; Request: TWebRequest;
Response: TWebResponse; var Handled: Boolean);
begin
PageProducer1.HTMLFile := 'Greeting.html';
Response.Content := PageProducer1.Content;
end;
Accueil.html est un fichier contenant ce modèle HTML :
<HTML>
<HEAD><TITLE>Notre nouveau site Web</TITLE></HEAD>
<BODY>
Hello <#UserName>! Bienvenue sur notre site.
</BODY>
</HTML>
Le gestionnaire d’événement OnHTMLTag remplace la balise personnalisée
(<#UserName>) dans le code HTML lors de l’exécution :
procedure WebModule1.PageProducer1HTMLTag(Sender : TObject;Tag: TTag;
const TagString: string; TagParams: TStrings; var ReplaceText: string );
begin
if CompareText(TagString,'UserName') = 0 then
ReplaceText := TPageProducer(Sender).Dispatcher.Request.Content;
end;
Si le contenu du message de requête était la chaîne Alain, la valeur de
Response.Content sera
<HTML>
<HEAD><TITLE>Notre nouveau site Web</TITLE></HEAD>
<BODY>
Bonjour Alain ! Bienvenue sur notre site.
</BODY>
</HTML>
Remarque Cet exemple utilise un gestionnaire d’événement OnAction pour appeler le
générateur de contenu et assigner le contenu de message de réponse. Vous
n’avez pas besoin d’écrire un gestionnaire d’événement OnAction si vous
assignez la propriété HTMLFile du générateur de page lors de la conception.
Dans ce cas, vous pouvez simplement assigner PageProducer1 comme valeur de la
propriété Producer de l’élément d’action pour obtenir le même effet que le
gestionnaire d’événement OnAction ci-dessus.

Chaînage de générateurs de page


Le texte de substitution venant du gestionnaire d’événement OnHTMLTag ne
doit pas nécessairement être la séquence HTML finale que vous voulez utiliser
dans le message de réponse HTTP. Vous pouvez utiliser plusieurs générateurs de
page, auquel cas le résultat de l’un sera passé au suivant.

Création d’applications serveur pour Internet 29-21


Génération du contenu des messages de réponse

La façon la plus simple de chaîner des générateurs de page consiste à associer


chaque générateur de page à un élément d’action particulier, tous les éléments
d’action disposant des mêmes PathInfo et MethodType. Le premier élément
d’action définit le contenu du message de réponse Web à partir de son
générateur de contenu, mais son gestionnaire d’événement OnAction veille à ce
que le message ne soit pas considéré comme traité. L’élément d’action suivant
utilise la méthode ContentFromString de son générateur associé pour manipuler le
contenu du message de réponse Web, etc. Les éléments d’action qui suivent le
premier élément d’action utilisent un gestionnaire d’événement comme suit :
procedure WebModule1.Action2Action(Sender: TObject; Request: TWebRequest;
Response: TWebResponse; var Handled: Boolean);
begin
Response.Content := PageProducer2.ContentFromString(Response.Content);
end;
Prenons comme exemple une application qui affiche des pages de calendrier en
réponse à des messages de requête indiquant le mois et l’année à afficher.
Chaque page de calendrier contient une image suivie de l‘année et du mois,
placés entre des images des mois précédent et suivant, puis du calendrier lui-
même. L’image finale aurait l’aspect suivant :

Le format général du calendrier réside dans un fichier de modèle à l’aspect


suivant :
<HTML>
<Head></HEAD>
<BODY>
<#MonthlyImage> <#TitleLine><#MainBody>
</BODY>
</HTML>
Le gestionnaire d’événement OnHTMLTag du premier générateur de page
recherche le mois et l’année dans le message de requête. Au moyen de ces
informations et du fichier de modèle, il :
• Remplace <#MonthlyImage> par <#Image Month=Janvier Year=1997>.
• Remplace <#TitleLine> par <#Calendar Month=Décembre Year=1996
Size=Small> Janvier 1997 <#Calendar Month=Février Year=1997 Size=Small>.
• Remplace <#MainBody> par <#Calendar Month=Janvier Year=1997
Size=Large>.

29-22 Guide du développeur


Utilisation des bases de données dans les réponses

Le gestionnaire d’événement OnHTMLTag du générateur de page suivant utilise


le contenu créé par le premier générateur de page et remplace la balise <#Image
Month=Janvier Year=1997> par la balise HTML <IMG> appropriée. Un troisième
générateur de page convertit les balises #Calendar en tableaux HTML.

Utilisation des bases de données dans les réponses


La réponse à un message de requête HTTP peut inclure des informations
extraites d’une base de données. Les générateurs de contenu spécialisés de la
page Internet sont capables de générer du code HTML pour représenter les
enregistrements d’une base de données dans un tableau HTML.
Sous une approche différente, les composants spéciaux de la page Web MIDAS
de la palette de composants vous permettent d’élaborer des serveurs Web faisant
partie d’une application de base de données multiniveau. Voir “Construction des
applications Web avec InternetExpress” à la page 14-34 pour plus de détails.

Ajout d’une session au module Web


Les applications console CGI et les applications Win-CGI sont lancées en réponse
à des messages de requête HTTP. Lorsque vous manipulez des bases de données
dans des applications de ce type, vous pouvez utiliser la session par défaut pour
gérer vos connexions aux bases de données car chaque message de requête
possède sa propre instance de l’application. Chaque instance de l’application
possède sa propre session par défaut.
Cependant, lorsque vous créez une application NSAPI ou ISAPI, chaque message
de requête est géré dans un thread distinct d’une instance d’application. Pour
empêcher que les connexions aux bases de données d’autres threads ne se
parasitent, vous devez donner une session distincte à chaque thread.
Chaque message de requête dans une application ISAPI ou NSAPI génère un
nouveau thread. Le module Web de ce thread est dynamiquement généré lors de
l’exécution. Ajoutez un objet TSession au module Web pour gérer les connexions
aux bases de données pour le thread qui contient le module Web.
A l’exécution, une instance distincte du module Web est générée pour chacun
des threads. Chacun de ces modules contient l’objet session. Chaque session doit
avoir un nom distinct pour que les threads qui gèrent les messages de requête
ne parasitent pas leur connexion aux bases de données respectives. Pour que les
objets session de chaque module s’attribuent dynamiquement un nom unique,
définissez la propriété AutoSessionName de l’objet session. Chaque session
s’attribuera dynamiquement un nom unique et définira la propriété SessionName
de tous les ensembles de données du module de façon à ce qu’elle fasse
référence à ce nom unique. Ceci permet l’interaction des threads de requête avec
la base de données, sans interférences avec les autres messages de requête. Pour
plus d’informations sur les sessions, voir chapitre 16, “Gestion de sessions de
bases de données.”.

Création d’applications serveur pour Internet 29-23


Utilisation des bases de données dans les réponses

Représentation HTML d’une base de données


Les commandes HTML offertes par les composants générateur de contenu
spécialisé de la page Internet de la palette des composants dépendent des
enregistrements contenus dans l’ensemble de données. Il existe deux types de
générateurs de contenu orientés données :
• Le générateur de page ensemble de données, qui formate les champs d'un
ensemble de données en texte d'un document HTML.
• Les générateurs de tableau, qui formatent les enregistrements d'un ensemble
de données sous forme de tableau HTML.

Utilisation des générateurs de page ensemble de données


Les générateurs de page ensemble de données fonctionnent comme les autres
composants générateur de page : ils convertissent un modèle incluant des balises
HTML transparentes en une représentation HTML finale. Cependant, ils
présentent la particularité de pouvoir convertir les balises dont le nom
correspond à celui d'un champ d'un ensemble de données en la valeur courante
de ce champ. Pour plus d'informations sur l'utilisation générale des générateurs
de page, voir “Utilisation du composant générateur de page” à la page 29-19.
Pour utiliser un générateur de page ensemble de données, ajoutez un composant
TDataSetPageProducer au module Web et attribuez à sa propriété DataSet
l'ensemble de données dont les valeurs de champ doivent être affichées dans le
contenu HTML. Créez un modèle HTML qui décrit le résultat de votre
générateur de page ensemble de données. Pour chaque valeur de champ à
afficher, incluez une balise de la forme
<#NomChamp>
dans le modèle HTML, où NomChamp spécifie le nom du champ de l'ensemble
de données dont la valeur doit être affichée.
Lorsque votre application appelle la méthode Content, ContentFromString ou
ContentFromStream, le générateur de page ensemble de données remplace les
balises représentant les champs par les valeurs courantes de ces derniers.

Utilisation des générateurs de tableau


La page Internet de la palette des composants offre deux composants qui
permettent de créer un tableau HTML représentant les enregistrements d'un
ensemble de données :
• Le générateur de tableau ensemble de données, qui formate les champs d'un
ensemble de données en texte d'un document HTML.
• Legénérateur de tableau requête, qui exécute une requête après avoir initialisé
les paramètres fournis par le message de requête et formate l’ensemble de
données obtenu en un tableau HTML.

29-24 Guide du développeur


Utilisation des bases de données dans les réponses

A partir de l’un ou l’autre des générateurs de tableau, vous pouvez personnaliser


l’aspect d’un tableau HTML obtenu en indiquant ses propriétés de couleur, de
bordure, de type de séparateur, etc. Pour définir les propriétés d’un générateur
de tableau à la conception, double-cliquez sur le composant générateur de
tableau pour afficher la boîte de dialogue Editeur de réponses.

Choix des attributs de tableau


Les générateurs de tableau utilisent l’objet THTMLTableAttributes pour décrire
l’aspect visuel du tableau HTML qui affiche les enregistrements de l’ensemble de
données. L’objet THTMLTableAttributes inclut les propriétés de largeur et
d’espacement du tableau dans le document HTML, ainsi que sa couleur de fond,
l’épaisseur de la bordure, l’alignement du texte dans les cellules et l’espacement
des cellules. Ces propriétés sont toutes converties en options de la balise HTML
<TABLE> créée par le générateur de tableau.
Spécifiez ces propriétés lors de la conception dans l’inspecteur d’objets.
Sélectionnez l’objet générateur de tableau et développez la propriété
TableAttributes pour afficher les propriétés de l’objet THTMLTableAttributes.
Vous pouvez également spécifier ces propriétés par programmation à l’exécution.

Choix des attributs de lignes


En plus des attributs de tableau, vous pouvez spécifier l’alignement et la couleur
du fond des lignes affichant des données. La propriété RowAttributes est un objet
THTMLTableRowAttributes.
Spécifiez ces propriétés lors de la conception dans l’inspecteur d’objets, en
développant la propriété RowAttributes. Vous pouvez également spécifier ces
propriétés par programmation à l’exécution.
Il est également possible de modifier le nombre de lignes du tableau HTML en
utilisant la propriété MaxRows.

Choix des attributs de colonnes


Si vous connaissez, au moment de la conception, l’ensemble de données à placer
dans le tableau, vous pouvez utiliser l’éditeur de colonnes pour personnaliser le
contenu et les attributs d’affichage des colonnes. Sélectionnez le composant
générateur de tableau et faites un clic sur le bouton droit de la souris. Dans le
menu contextuel, choisissez Editeur de colonnes. Ceci vous permet d’ajouter, de
supprimer et de déplacer les colonnes du tableau. Vous pouvez définir les
champs à placer et les propriétés d’affichage des colonnes dans l’inspecteur
d’objets après les avoir sélectionnées dans l’éditeur de colonnes.
Si le nom de l’ensemble de données figure dans le message de requête HTTP,
vous ne pouvez pas définir les champs à placer depuis l’éditeur de colonnes en
phase de conception. Vous pouvez cependant personnaliser les colonnes par
programmation lors de l’exécution en définissant les objets THTMLTableColumn
correspondants et en utilisant les méthodes de la propriété Columns pour ajouter
ces colonnes au tableau. Si vous ne vous servez pas de la propriété Columns, le
générateur de tableau créera des colonnes par défaut adaptées aux champs de
l’ensemble de données, mais ne spécifiera aucune caractéristique d’affichage.

Création d’applications serveur pour Internet 29-25


Utilisation des bases de données dans les réponses

Incorporation de tableaux dans un document HTML


Vous pouvez incorporer le tableau HTML représentant votre ensemble de
données dans un document HTML à l’aide des propriétés Header et Footer du
générateur de tableau. La propriété Header permet d’indiquer quelles
informations doivent être affichées avant le tableau, alors que Footer spécifie ce
qui vient après.
Il est possible d’utiliser un autre générateur de contenu (tel qu’un générateur de
tableau) pour créer les valeurs des propriétés Header et Footer.
Lorsque vous incorporez votre tableau dans un document distinct, il est possible
d’ajouter une légende au tableau à l’aide des propriétés Caption et
CaptionAlignment.

Configuration d’un générateur de tableau ensemble de


données
TDataSetTableProducer est un générateur de tableau qui permet de créer un
tableau HTML pour un ensemble de données. Définissez la propriété DataSet de
TDataSetTableProducer pour désigner l’ensemble de données contenant les
enregistrements à afficher. Contrairement à la procédure normale pour la plupart
des objets orientés données d’une application de bases de données
conventionnelle, la propriété DataSource n’a pas à être définie. Ceci s’explique
par le fait que TDataSetTableProducer génère sa propre source de données en
interne.
Vous pouvez choisir la valeur de DataSet en phase de conception si votre
application Web affiche toujours des enregistrements issus du même ensemble de
données. Vous devez définir la propriété DataSet lors de l’exécution si l’ensemble
de données varie en fonction des informations contenues dans le message de
requête HTTP.

Configuration d’un générateur de tableau requête


Vous pouvez générer un tableau HTML pour afficher les résultats d’une requête,
dans lequel les paramètres de recherche viennent du message HTTP. Désignez
l’objet TQuery qui utilise ces paramètres comme propriété Query d’un composant
TQueryTableProducer.
Si le message est une requête GET, les paramètres de recherche sont situés dans
les champs Query de l’URL fourni comme destination du message de requête
HTTP. Si le message est une requête POST, ces paramètres sont dans le contenu
du message de requête.
Lorsque vous appelez la méthode Content de TQueryTableProducer, elle exécute
l’interrogation à l’aide des paramètres figurant dans l’objet requête. Elle formate
ensuite un tableau HTML pour représenter les enregistrements dans l’ensemble
de données obtenu.
Comme avec tout générateur de tableau, vous pouvez personnaliser les
propriétés d’affichage ou de contenu des colonnes du tableau HTML. Il est
également possible d’incorporer le tableau dans un autre document HTML.

29-26 Guide du développeur


Débogage d’applications serveur

Débogage d’applications serveur


Le débogage des applications serveur Web présente des problèmes spécifiques
car l’exécution de ces applications est influencée par les messages qu’elles
reçoivent du serveur Web. Lancer votre application depuis l’EDI n’est pas
suffisant, car cela ne tiendrait pas compte du serveur Web et votre application ne
trouverait pas les messages de requêtes qu’elle attend. La façon dont vous
déboguez votre application serveur Web dépend de son type.

Débogage d’applications ISAPI et NSAPI


Les applications ISAPI et NSAPI sont en fait des DLL qui contiennent des points
d’entrée prédéfinis. Le serveur Web passe les messages de requête à l’application
en faisant des appels à ces points d’entrée. Vous devrez définir les paramètres
d’exécution de votre application de telle façon qu’elle lance le serveur. Définissez
vos points d’arrêt de telle façon que, lorsque le serveur passe un message de
requête à votre DLL, un point d’arrêt soit activé et que vous puissiez effectuer le
débogage.
Remarque Avant de lancer le serveur Web avec les paramètres d’exécution de votre
application, vérifiez qu’il n’est pas déjà ouvert.

Débogage sous Windows NT


Sous Windows NT, vous devez avoir certains droits d’utilisateur pour pouvoir
déboguer une DLL. Dans le gestionnaire des utilisateurs, ajoutez votre nom aux
listes accordant les droits pour :
• Ouvrir une session en tant que service
• Agir en tant que composante du système d’exploitation
• Générer des audits de sécurité

Débogage avec Microsoft IIS Server


Pour déboguer une application serveur Web à l’aide de Microsoft IIS server,
(version 3 ou antérieure) choisissez Exécuter|Paramètres et entrez les paramètres
d’exécution suivants pour votre application :
Application hôte : c:\winnt\system32\inetsrv\inetinfo.exe
Paramètres : -e w3svc
Ceci lance le serveur IIS et vous permet de déboguer votre DLL ISAPI.
Remarque L’emplacement de votre fichier inetinfo.exe peut être différent.
Si vous utilisez la version 4 ou ultérieure , vous devez d’abord apporter certaines
modifications au registre et au service IIS Administration :
1 Utilisez DCOMCnfg pour attribuer à l’identité du service IIS Admin votre
compte d’utilisateur.

Création d’applications serveur pour Internet 29-27


Débogage d’applications serveur

2 Utilisez l’éditeur de la base de registres (REGEDIT), ou un autre utilitaire,


pour supprimer le mot clé “LocalService” de toutes les clés secondaires de
IISADMIN sous HKEY_CLASSES_ROOT/AppID et HKEY_CLASSES_ROOT/
CLSID. Ce mot clé apparaît dans les clés secondaires suivantes :
{61738644-F196-11D0-9953-00C04FD919C1} // IIS WAMREG admin Service
{9F0BD3A0-EC01-11D0-A6A0-00A0C922E752} // IIS Admin Crypto Extension
{A9E69610-B80D-11D0-B9B9-00A0C922E750} // IISADMIN Service
Par ailleurs, sous le noeud AppID, supprimez le mot clé “RunAs” des deux
premières de ces clés secondaires. Dans la dernière clé secondaire répertoriée,
ajoutez “Interactive User” comme valeur du mot-clé “RunAs”.
3 Toujours à l'aide de l'éditeur de la base de registres, ajoutez les clés
secondaires "LocalService32" à toutes les clés secondaires IISADMIN sous le
noeud CLSID. Ainsi, pour chaque clé secondaire répertoriée à l'étape 2 (et
pour toutes celles sous lesquelles figure le mot clé "LocalService"), ajoutez une
clé secondaire "LocalService32" sous le noeud CLSID/<clé secondaire>.
Définissez la valeur par défaut de ces nouvelles clés à
c:\\winnt\\system32\\inetsrv\\inetinfo.exe -e w3svc
(l’emplacement de votre fichier inetinfo.exe peut être différent).
4 Ajoutez la valeur “dword:3” au mot clé “Start” des clés secondaires suivantes :
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\IISADMIN]
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\MSDTC]
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC]
5 Arrêtez les services WWW, FTP et IISAdmin à partir de la console de gestion
Microsoft ou de la boîte de dialogue Services dans le panneau de
configuration. Vous pouvez aussi utiliser la commande KILL INETINFO à
l’aide de l’utilitaire KILL.EXE disponible dans le kit de ressources NT.
Vous êtes désormais en mesure de déboguer de la même manière que sous IIS
version 3 ou antérieure : choisissez Exécuter|Paramètres et définissez les
paramètres d'exécution de votre application comme suit :
Application hôte : c:\winnt\system32\inetsrv\inetinfo.exe
Paramètres : -e w3svc
Remarque Lorsque vous avez fini de déboguer, vous devez supprimer toutes les
modifications apportées à la base de registres aux étapes 2 à 4.

Débogage sous MTS


Sous IIS, une autre approche consiste à configurer votre répertoire Web comme
un paquet de bibliothèque MTS. Vous pouvez alors déboguer votre dll ISAPI en
l'exécutant sous MTS.
Pour configurer le répertoire Web comme un paquet de bibliothèque MTS,
procédez comme suit :
1 Démarrez le gestionnaire de services Internet. Les deux arborescences Internet
Information Server et Microsoft Transaction Server doivent être affichées.

29-28 Guide du développeur


Débogage d’applications serveur

2 Développez l'arborescence Internet Information Server pour visualiser les


éléments sous le site Web par défaut. Sélectionnez le répertoire Web dans lequel
votre dll ISAPI est installée. Cliquez avec le bouton droit et choisissez Propriétés.
3 Sur la page d’onglet du répertoire virtuel, activez l’option d’exécution dans un
espace mémoire distinct (processus isolé), et cliquez sur OK.
4 Développez l'arborescence Microsoft Transaction Server pour visualiser les
éléments sous le noeud des paquets installés. Cliquez avec le bouton droit sur
le noeud des paquets installés et sélectionnez l’option d’actualisation.
5 Un paquet portant le même suffixe que le répertoire Web apparaît. Cliquez
dessus avec le bouton droit et choisissez Propriétés.
6 Sur la page d’onglet d’identité, activez le bouton radio d’utilisateur interactif
et cliquez sur OK.
Les étapes précédentes vous permettent de configurer votre répertoire Web. Une
fois que cela est fait, vous pouvez déboguer votre DLL ISAPI comme suit :
1 Dans Delphi, choisissez Exécuter|Paramètres. Dans le champ Application hôte,
entrez le chemin complet de l’exécutable MTS. Habituellement, il s’agit de :
c:\winnt\system32\mtx.exe
2 Dans le champ de paramètres, vous devez utiliser l'option /p avec le nom du
paquet MTS. Pour obtenir cette valeur, démarrez le gestionnaire de services
Internet et développez l'arborescence Microsoft Transaction Server pour
visualiser les éléments sous le noeud des paquets installés. Cliquez avec le
bouton droit sur le paquet portant le même suffixe que le répertoire Web.
Choisissez Propriétés, et copiez dans le Presse-papiers le nom du paquet à
partir de la page d'onglet Général.
Dans le champ de paramètres, collez le nom du paquet, placez des guillemets
avant et après, puis l'option /p: avant l'expression entre guillemets. Le champ
de paramètres obtenu doit ressembler à ceci :
/p:“IIS-{Default Web Site//ROOT/WEBPUB/DEMO}”
Notez qu'aucun espace ne doit figurer entre les deux-points et le nom de paquet.
Conseil Lorsque le répertoire Web est installé en tant que paquet MTS, vous pouvez aussi
utiliser MTS pour arrêter facilement la DLL. Pour ce faire, développez l'arborescence
Microsoft Transaction Server dans le gestionnaire de services Internet afin d'afficher
les éléments sous le noeud des paquets installés. Cliquez avec le bouton droit sur
le paquet portant le même suffixe que le répertoire Web et choisissez l’option d’arrêt.

Débogage avec Personal Web Server pour Windows 95


Pour déboguer une application serveur Web à l’aide de Personal Web Server,
entrez les paramètres d’exécution suivants pour votre application :
Application hôte : c:\Program Files\websvc\system\inetsw95.exe
Paramètres : -w3svc
Ceci lance Personal Web Server et vous permet de déboguer votre DLL ISAPI.
Remarque L’emplacement de votre fichier inetsw95.exe peut être différent.

Création d’applications serveur pour Internet 29-29


Débogage d’applications serveur

Débogage avec Netscape Server Version 2.0


Avant d’utiliser les applications serveur Web sur des serveurs Netscape, vous
devez modifier la configuration.
D’abord, le fichier ISAPITER.DLL (depuis le répertoire Bin) dans le répertoire
C:\Netscape\Server\Nsapi\Examples (il se peut que le nom de votre répertoire
soit différent).
Ensuite, modifiez comme suit les fichiers de configuration du serveur situés dans
le répertoire C:\Netscape\Server\Httpd-<nom_du_serveur>\Config.
1 Dans le fichier OBJ.CONF, insérez la ligne :
Init funcs="handle_isapi,check_isapi,log_isapi" fn="load_modules"
shlib="c:/netscape/server/nsapi/examples/ISAPIter.dll"
après la ligne :
Init fn=load-types mime-types=mime.types
2 Dans la section <Object name=default> de OBJ.CONF, insérez les lignes
suivante :
NameTrans from="/scripts" fn="pfx2dir" dir="C:/Netscape/Server/docs/scripts"
name="isapi"
avant la ligne :
NameTrans fn=document-root root="C:/Netscape/Server/docs"
3 Ajoutez la section suivante à la fin de OBJ.CONF :
<Object name="isapi">
PathCheck fn="check_isapi"
ObjectType fn="force-type" type="magnus-internal/isapi"
Service fn="handle_isapi"
</Object>
4 Ajoutez la ligne suivante à la fin du fichier MIME.TYPES :
type=magnus-internal/isapi exts=dll
Cette ligne doit être la dernière ligne du fichier.
Remarque Des sauts de ligne figurent aux étapes 1 et 2 ci-dessus uniquement pour faciliter
la lecture du code. N’insérez pas de retours chariot lorsque vous entrez ces
lignes dans les fichiers de configuration.
Pour déboguer une application serveur Web avec Netscape Fast Track Server,
définissez les paramètres d’exécution de l’application de la façon suivante :
Host Application: c:\Netscape\server\bin\httpd\httpd.exe
Run Parameters: c:\Netscape\server\httpd-<servername>\config
Ceci lance le serveur et lui indique où se trouvent les fichiers de configuration.

29-30 Guide du développeur


Débogage d’applications serveur

Débogage d’applications CGI et Win-CGI


Il est plus délicat de déboguer des applications CGI et Win-CGI car l’application
elle-même doit être lancée par le serveur Web.

Simulation du serveur
Pour les applications Win-CGI, vous pouvez simuler le serveur en créant le
fichier de configuration contenant les informations de requête. Lancez ensuite
l’application Win-CGI en lui passant l’emplacement du fichier contenant les
informations sur le client et l’emplacement du fichier que le programme Win-
CGI doit utiliser pour créer le contenu. Vous pouvez ensuite déboguer
l’application.

Débogage en tant que DLL


Une autre approche consiste, pour les applications CGI et Win-CGI, à
commencer le débogage de votre application comme une application ISAPI ou
NSAPI. Lorsqu’elle fonctionne bien, convertissez votre application en application
CGI ou Win-CGI. Pour convertir votre application, procédez de la manière
suivante :
1 Cliquez avec le bouton droit de la souris dans le module Web et choisissez
Ajouter au référentiel.
2 Dans la boîte de dialogue Ajout au référentiel, spécifiez le nom, la description,
la page de référentiel (généralement Modules de données), le nom de l’auteur
et l’icône du module Web.
3 Choisissez OK pour enregistrer le module Web comme modèle.
4 Dans le menu principal, choisissez Fichier|Nouveau et sélectionnez
Application serveur web. Dans la boîte de dialogue Nouvelle application
serveur web, choisissez CGI ou Win-CGI.
5 Supprimez le module Web généré automatiquement.
6 Dans le menu principal, choisissez Fichier|Nouveau et sélectionnez le modèle
enregistré à l’étape 3. Il se trouve dans la page spécifiée à l’étape 2.
Les applications CGI et Win-CGI sont plus simples que les applications ISAPI et
NSAPI. Chaque instance d’une application CGI ou Win-CGI ne doit gérer qu’un
seul thread. Ainsi, ces applications n’ont pas les problèmes de threads multiples
qu’ont les applications ISAPI et NSAPI. Elles n’ont pas non plus à gérer les
problèmes liés à la mise en mémoire cache des modules Web, problèmes que
connaissent les applications ISAPI et NSAPI.

Création d’applications serveur pour Internet 29-31


29-32 Guide du développeur
Chapitre

Utilisation des sockets


Chapter 30
30
Les composants socket vous permettent de créer une application pouvant
communiquer avec d’autres systèmes par TCP/IP et ses protocoles associés. A
l’aide des sockets, vous pouvez lire et écrire sur des connexions à d’autres
machines sans vous soucier des détails concernant le logiciel réseau. Les sockets
offrent des connexions basées sur le protocole TCP/IP et sont assez génériques
pour fonctionner avec des protocoles tels que Xerox Network System (XNS),
DECnet de Digital ou la famille IPX/SPX de Novell.
L’utilisation des sockets vous permet d’écrire des applications serveur ou client
qui lisent et écrivent sur des systèmes distants. Une application serveur ou client
est en général dédiée à un service unique tel que HTTP (Hypertext Transfer
Protocol) ou FTP (File Transfer Protocol). En utilisant les sockets serveur, une
application offrant l’un de ces services peut se lier aux applications client qui
souhaitent utiliser ce service. Les sockets client permettent à une application
utilisant l’un de ces services de se lier à des applications serveur offrant ce
service.

Implémentation des services


Les sockets offrent l’un des composants dont vous avez besoin pour créer des
applications serveur ou client. Pour beaucoup de services, tels que HTTP ou FTP,
des serveurs développés par diverses sociétés sont disponibles. Certains sont
même fournis en standard avec le système d’exploitation, ce qui vous évite d’en
créer un vous-même. Cependant, si vous désirez affiner la façon dont le service
est implémenté (pour obtenir, par exemple, une meilleure intégration de votre
application et de la communication réseau) ou si aucun serveur n’est disponible
pour le service précis dont vous avez besoin, vous devrez créer votre propre
application serveur ou client. Par exemple, lorsque vous manipulez des
ensembles de données distribués, vous pouvez créer une couche pour
communiquer avec des bases de données sur les systèmes distants.

Utilisation des sockets 30-1


Types de connexions par socket

Description des protocoles de services


Avant de créer un serveur ou un client réseau, vous devez comprendre le service
que votre application offrira ou utilisera. La majorité des services ont des
protocoles standard que votre application doit supporter. Si vous créez une
application réseau pour un service standard tel que HTTP, FTP, Finger ou Time,
vous devez comprendre les protocoles utilisés pour communiquer avec les
systèmes distants. Consultez la documentation se rapportant au service que vous
comptez offrir ou utiliser.
Si vous offrez un nouveau service pour une application qui communique avec
des systèmes distants, la première étape consiste à concevoir le protocole de
communication pour les serveurs et les clients de ce service. Quels messages sont
envoyés ? Comment ces messages sont-ils structurés ? Comme les informations
sont-elles codées ?

Communication avec les applications


Souvent, votre application serveur ou client offre une couche entre le logiciel
réseau et l’application qui utilise le service. Par exemple, un serveur HTTP est
placé entre Internet et l’application serveur Web qui fournit le contenu et gère
les messages de demandes HTTP.
Les sockets sont l’interface entre votre application serveur ou client et le logiciel
réseau. Vous devez fournir l’interface entre votre application et les applications
qui l’utilisent. Vous pouvez copier l’API d’un serveur standard existant (ISAPI,
par exemple) ou concevoir et publier votre propre API.

Services et ports
La plupart des services standard sont associés, par convention, à des numéros de
ports précis. Nous étudierons plus tard ces numéros de ports. Pour l’instant,
considérez le numéro de port comme un code numérique pour ce service.
Si vous implémentez un service standard, les objets socket Windows fournissent
des méthodes de recherche de numéro de port pour le service. Si vous offrez un
service nouveau, vous pouvez spécifier son numéro de port dans un fichier
SERVICES sur les systèmes sous Windows 95 ou NT. Consultez la
documentation Microsoft traitant des sockets Windows pour de plus amples
informations sur le contenu du fichier SERVICES.

Types de connexions par socket


Les connexions par socket peuvent être scindées en trois groupes principaux
indiquant la façon dont la connexion a été ouverte et le type de connexion :
• Connexions client
• Connexions d’écoute
• Connexions serveur

30-2 Guide du développeur


Description des sockets

Lorsque la connexion au socket client est effective, la connexion serveur est


identique à une connexion client. Les deux extrémités ont les mêmes possibilités
et reçoivent des événements de même type. Seule la connexion d’écoute est
fondamentalement différente, car elle ne comporte qu’une extrémité.

Connexions client
Les connexions client connectent un socket client sur le système local à un socket
serveur sur un système distant. Les connexions client sont lancées par le socket
client. En premier lieu, le socket client doit décrire le socket serveur auquel il
souhaite se connecter. Le socket client recherche ensuite le socket serveur et,
lorsqu’il l’a trouvé, demande une connexion. Le socket serveur peut ne pas
établir immédiatement la connexion. Les sockets serveur gèrent une file d’attente
des demandes de clients et établissent la connexion lorsqu’ils le peuvent. Lorsque
le socket serveur accepte la connexion du client, il envoie au socket client une
description complète du socket serveur auquel il se connecte et la connexion est
finalisée par le client.

Connexions d’écoute
Les sockets serveur ne localisent pas les clients : ils génèrent des “demi-
connexions” passives qui restent à l’écoute des requêtes des clients. Les sockets
serveur associent une file d’attente à leurs connexions d’écoute ; la file d’attente
enregistre les requêtes de connexion lorsqu’elles lui parviennent. Lorsque le
socket serveur accepte une demande de connexion client, il forme un nouveau
socket pour se connecter au client pour que la connexion d’écoute reste ouverte
afin d’accepter d’autres requêtes de clients.

Connexions serveur
Les connexions serveur sont formées par des sockets serveur lorsque le socket
d’écoute accepte une requête du client. La description du socket serveur ayant
effectué la connexion au client est envoyée au client lorsque le serveur accepte la
connexion. La connexion est établie lorsque le socket client reçoit cette
description et effectue véritablement la connexion.

Description des sockets


Les sockets permettent à votre application de communiquer avec des systèmes
distants par le biais d’un réseau. Chaque socket peut être considéré comme un
point de terminaison dans une connexion réseau. Il possède une adresse qui
spécifie :
• le système sur lequel il s’exécute ;
• les types d’interfaces qu’il comprend ;
• le port qu’il utilise pour la connexion.

Utilisation des sockets 30-3


Description des sockets

Une description complète d’une connexion par socket inclut les adresses du socket
à chaque extrémité de la connexion. Vous pouvez décrire l’adresse de chaque
extrémité du socket en fournissant l’hôte ou l’adresse IP et le numéro de port.
Avant de faire une connexion par socket, vous devez décrire complètement les
sockets formant ses extrémités. Certaines informations sont disponibles sur le
système exécutant votre application. Par exemple, il n’est pas nécessaire de
décrire l’adresse IP locale d’un socket client car elle est dans le système
d’exploitation.
Les informations à fournir dépendent du type de socket implémenté. Les sockets
client doivent décrire le serveur auquel ils souhaitent se connecter. Les sockets
serveur d’écoute doivent décrire le port représentant le service qu’ils offrent.

Description des hôtes


L’hôte est le système qui exécute l’application contenant le socket. Vous pouvez
décrire les hôtes à un socket en fournissant son adresse IP, qui consiste en une
chaîne de quatre valeurs numériques au format Internet standard, par exemple :
123.197.1.2
Un système peut gérer plusieurs adresses IP.
Les adresses IP sont difficiles à mémoriser. L’alternative consiste à utiliser le
nom de l’hôte. Les noms d’hôtes sont des alias d’adresses IP exprimées au
format URL (Uniform Resource Locator). Une chaîne d’URL comporte un nom
de domaine et un service, par exemple :
http://www.wSite.Com
La majorité des intranets fournit des noms d’hôtes pour les adresses IP des
systèmes sur Internet. Sous Windows 95 et NT, si le nom de l’hôte n’est pas
disponible, vous pouvez en créer un pour votre adresse IP locale en entrant son
nom dans le fichier des hôtes (ce fichier porte le nom “HOSTS”). Reportez-vous
à la documentation Microsoft sur les sockets Windows pour plus d’informations
sur ce fichier.
Les sockets serveur n’ont pas besoin de spécifier d’hôtes. L’adresse IP locale peut
être obtenue auprès du système. Si le système local gère plusieurs adresses IP,
les sockets écoutent les requêtes client sur toutes ces adresses en même temps.
Lorsqu’un socket serveur accepte une connexion, le socket client indique
l’adresse IP du système distant.
Les sockets client doivent spécifier les hôtes distants en fournissant leur nom ou
leur adresse IP.

Choix entre le nom de l’hôte et son adresse IP


La majorité des applications utilise un nom d’hôte pour désigner un système. Les
noms d’hôtes sont en effet plus faciles à mémoriser. De plus, les serveurs
peuvent changer l’adresse IP ou le système associé à un nom d’hôte précis. Le
fait d’utiliser les noms d’hôtes permet au socket client de trouver le site abstrait
représenté par le nom d’hôte même s’il a changé d’adresse IP.

30-4 Guide du développeur


Utilisation des composants socket

Si le nom de l’hôte vous est inconnu, le socket client doit spécifier le système
serveur par son adresse IP. Ce processus donne des recherches plus rapides car,
lorsque vous spécifiez un nom d’hôte, le socket doit rechercher l’adresse IP
associée à ce nom pour localiser le système serveur.

Utilisation des ports


Même si une adresse IP contient assez d’informations pour trouver le système à
l’autre bout de la connexion socket, vous devez également indiquer un numéro
de port sur ce système. Sans les numéros de port, un système ne pourrait former
qu’une connexion à la fois. Les numéros de port sont des identificateurs uniques
permettant à un ordinateur d’accepter plusieurs connexions simultanées en
attribuant à chaque connexion un numéro de port distinct.
Nous avons décrit précédemment les numéros de port comme des codes
numériques pour les services implémentés par les applications réseau. Il s’agit
uniquement d’une convention permettant aux connexions serveur d’écoute de se
libérer sur un numéro de port fixe pour qu’elles puissent être trouvées par les
sockets client. Les sockets serveur écoutent sur le numéro de port associé au
service qu’ils offrent. Lorsqu’ils acceptent une connexion à un socket client, ils
créent une connexion socket distincte utilisant un autre numéro de port attribué
arbitrairement. De cette façon, la connexion d’écoute peut rester vigilante sur le
numéro de port associé au service.
Les sockets client utilisent un numéro de port local arbitraire car ils n’ont pas
besoin d’être détectés par les autres sockets. Ils spécifient le numéro de port du
socket serveur auquel ils désirent se connecter pour pouvoir trouver l’application
serveur. Souvent, ce numéro de port est spécifié indirectement en nommant le
service souhaité.

Utilisation des composants socket


La page Internet de la palette des composants inclut deux composants socket
(sockets client et sockets serveur) permettant à votre application réseau de
former une connexion à d’autres machines et vous permettant de lire et d’écrire
des informations sur cette connexion. Des objets socket Windows sont associés à
chacun de ces composants socket, et représentent l’extrémité d’une connexion
socket existante. Les composants socket utilisent les objets socket Windows pour
encapsuler les appels d’API socket de Windows, pour que votre application n’ait
pas à connaître les détails de l’établissement de la connexion ou de la gestion
des messages du socket.
Si vous souhaitez utiliser les appels d’API de socket Windows ou personnaliser
les détails des connexions qu’un composant socket effectue pour vous, vous
pouvez utiliser les propriétés, les événements et les méthodes des objets socket
Windows.

Utilisation des sockets 30-5


Utilisation des composants socket

Utilisation de sockets client


Ajoutez un composant socket client (TClientSocket) à votre fiche ou à votre
module de données pour transformer votre application en client TCP/IP. Les
sockets client vous permettent de spécifier le socket serveur auquel vous
souhaitez vous connecter et le service que vous attendez de ce serveur. Lorsque
vous avez décrit la connexion voulue, vous pouvez utiliser le composant socket
client pour établir la connexion au serveur.
Chaque composant socket client utilise un objet socket client Windows unique
(TClientWinSocket) pour représenter l’extrémité client dans une connexion.

Désignation du serveur souhaité


Les composants socket client ont de nombreuses propriétés qui vous permettent
de spécifier le système serveur et le port auxquels vous souhaitez vous
connecter. Vous pouvez spécifier le serveur par son nom d’hôte à l’aide de la
propriété Host. Si vous ne connaissez pas son nom, ou si vous souhaitez
accélérer la recherche du serveur, vous pouvez spécifier son adresse IP à l’aide
de la propriété Address. Vous devez spécifier soit un nom d’hôte, soit une
adresse IP. Si vous spécifiez les deux, le composant socket client utilise le nom
d’hôte.
En plus du système serveur, vous devez spécifier le port du système serveur sur
lequel votre socket client se connectera. Vous pouvez spécifier ce numéro de port
à l’aide de la propriété Port ou en nommant le service dans la propriété Service.
Si vous spécifiez les deux paramètres, le composant socket client utilise le nom
du service.

Formation de la connexion
Lorsque les propriétés de votre composant socket client décrivent le serveur
auquel vous souhaitez vous connecter, vous pouvez former la connexion à
l’exécution en appelant la méthode Open. Si vous souhaitez que votre application
forme automatiquement la connexion à son ouverture, mettez la propriété Active
à True lors de la conception, à l’aide de l’inspecteur d’objets.

Obtention d’informations sur la connexion


Lorsque vous avez ouvert une connexion d’écoute avec votre socket serveur,
vous pouvez utiliser l’objet socket serveur Windows associé à votre composant
socket serveur pour obtenir des informations sur la connexion. Utilisez la
propriété Socket pour accéder à l’objet socket serveur Windows. Cet objet
comporte des propriétés qui vous permettent de connaître l’adresse et le numéro
de port utilisés par les sockets client et serveur pour former les extrémités de la
connexion. Utilisez la propriété SocketHandle pour obtenir un handle sur la
connexion socket à utiliser lorsque vous faites des appels d’API de socket
Windows. Vous pouvez utiliser la propriété Handle pour accéder à la fenêtre qui
reçoit les messages émanant de la connexion socket. La propriété ASyncStyles
détermine les types de messages que le handle de fenêtre reçoit.

30-6 Guide du développeur


Utilisation des composants socket

Fermeture de la connexion
Lorsque vous avez fini de communiquer avec une application serveur sur la
connexion socket, vous pouvez fermer la connexion en appelant la méthode
Close. La connexion peut également être fermée depuis le serveur. Si c’est le cas,
vous en êtes informé par un événement OnDisconnect.

Utilisation de sockets serveur


Ajoutez un composant socket serveur (TServerSocket) à votre fiche ou à votre
module de données pour transformer votre application en serveur TCP/IP. Les
sockets serveur vous permettent de spécifier le service que vous offrez ou le port
que vous utilisez pour écouter les requêtes client. Vous pouvez utiliser le
composant socket serveur pour écouter et accepter les requêtes de connexion
client.
Chaque composant socket serveur utilise un objet socket serveur Windows
unique (TServerWinSocket) pour représenter l’extrémité serveur dans une
connexion d’écoute. Il utilise également un objet client (TServerClientWinSocket)
pour l’extrémité serveur de chaque connexion active à un socket client et
acceptée par le serveur.

Désignation du port
Pour que votre socket serveur puisse écouter les requêtes de connexion client,
vous devez spécifier un port d’écoute. Vous pouvez spécifier ce port à l’aide de
la propriété Port. Si votre application serveur offre un service standard associé
par convention à un numéro de port précis, vous pouvez spécifier ce numéro de
port indirectement, à l’aide de la propriété Service. Il est recommandé d’utiliser la
propriété Service car il est possible de faire des fautes de saisie en entrant le
numéro de port. Si vous précisez à la fois les propriétés Port et Service, le socket
serveur utilisera le nom du service.

Ecoute des requêtes client


Lorsque vous avez défini le numéro de port de votre composant socket serveur,
vous pouvez former une connexion d’écoute à l’exécution en appelant la
méthode Open. Si vous souhaitez que cette connexion soit formée
automatiquement au lancement de l’application, mettez la propriété Active à True
lors de la conception, à l’aide de l’inspecteur d’objets.

Connexion aux clients


Un composant socket serveur d’écoute accepte automatiquement les requêtes de
connexion des clients. Vous en recevez notification dans un événement
OnClientConnect.

Utilisation des sockets 30-7


Réponse aux événements socket

Obtenir des informations sur les connexions


Lorsque vous avez ouvert une connexion d’écoute avec votre socket serveur,
vous pouvez utiliser l’objet socket serveur Windows associé à votre composant
socket serveur pour obtenir des informations sur la connexion. Utilisez la
propriété Socket pour accéder à l’objet socket serveur Windows. Cet objet a des
propriétés qui vous permettent d’en savoir plus sur les connexions actives avec
des sockets client qui ont été acceptées par votre composant socket serveur.
Utilisez la propriété SocketHandle pour obtenir un handle vers la connexion
socket à utiliser lorsque vous faites des appels d’API de socket Windows.
Utilisez la propriété Handle pour accéder à la fenêtre qui reçoit les messages
émanant de la connexion socket.
Chaque connexion active à une application client est encapsulée par un objet
socket Windows client serveur (TServerClientWinSocket). Vous pouvez accéder à
toutes ces connexions par la propriété Connections de l’objet socket serveur
Windows. Ces objets socket Windows client serveur ont des propriétés qui vous
permettent de déterminer l’adresse et le numéro de port utilisés par les sockets
client et serveur qui forment les extrémités de la connexion. Vous pouvez utiliser
la propriété SocketHandle pour obtenir un handle sur la connexion socket à
utiliser lorsque vous faites des appels à l’API socket de Windows. Vous pouvez
utiliser la propriété Handle pour accéder à la fenêtre recevant les messages de la
connexion socket. La propriété ASyncStyles détermine les types de messages que
le handle de fenêtre reçoit.

Fermeture des connexions serveur


Lorsque vous souhaitez fermer la connexion d’écoute, appelez la méthode Close.
Ceci ferme toutes les connexions établies avec les applications client, annule les
connexions en attente non encore acceptées et met fin à la connexion d’écoute,
après quoi votre composant socket serveur n’accepte pas de nouvelles
connexions.
Lorsque les clients mettent fin à leur connexion à votre socket serveur, vous en
êtes informé par un événement OnClientDisconnect.

Réponse aux événements socket


Dans des applications utilisant les sockets, l’essentiel du travail se fait
généralement dans les gestionnaires d’événements des composants socket.
Certains sockets génèrent des événements quand c’est le moment de lire ou
d’écrire via la connexion de socket. Pour des informations, voir “Lecture et
écriture d’événements” à la page 30-11.
Les sockets client reçoivent un événement OnDisconnect lorsque le serveur
termine une connexion, et les sockets serveur reçoivent un événement
OnClientDisconnect lorsque le client termine une connexion.
Les sockets client et les sockets serveur génèrent tous deux des événements
d’erreurs lorsqu’ils reçoivent des messages d’erreur émis par la connexion.

30-8 Guide du développeur


Réponse aux événements socket

Les composants socket reçoivent également divers événements durant la


connexion. Si votre application doit influer sur l’ouverture du socket ou si elle
doit lancer la lecture ou l’écriture lorsque la connexion est formée, il est conseillé
d’écrire des gestionnaires d’événements pour répondre à ces événements client
ou événements serveur.

Evénements d’erreurs
Les sockets client génèrent un événement OnError lorsqu’ils reçoivent un
message d’erreur émis par la connexion. Les sockets serveur génèrent un
événement OnClientError.Vous pouvez écrire un gestionnaire d’événement
OnError ou OnClientError pour répondre à ces messages d’erreur. Le gestionnaire
d’événement reçoit des informations sur :
• l’objet socket Windows ayant reçu la notification d’erreur ;
• ce que le socket tentait de faire lorsque l’erreur s’est produite ;
• le code d’erreur fourni par le message d’erreur.
Vous pouvez répondre à l’erreur dans le gestionnaire d’événement et mettre le
code d’erreur à 0 pour empêcher le socket de déclencher une exception.

Evénements client
Lorsqu’un socket client ouvre une connexion, les événements suivants se
produisent :
1 Un événement OnLookup se produit avant une tentative de localisation du
socket serveur. A ce moment-là, vous ne pouvez pas modifier les propriétés
Host, Address, Port ou Service pour changer le socket serveur qui est localisé.
Vous pouvez utiliser la propriété Socket pour accéder à l’objet socket
Windows client et utiliser sa propriété SocketHandle pour faire des appels
d’API Windows qui affectent les propriétés client du socket. Par exemple, si
vous souhaitez définir le numéro de port sur l’application client, faites-le
avant que le serveur client ne soit contacté.
2 Le socket Windows est paramétré et initialisé pour la notification
d’événements.
3 Un événement OnConnecting se produit après que le serveur socket soit
localisé. A ce moment, l’objet Windows Socket accessible par la propriété
Socket peut fournir des informations sur le serveur socket qui sera à l’autre
extrémité de la connexion. Ceci est la première possibilité d’obtenir le numéro
de port et l’adresse IP utilisés pour la connexion, qui peuvent différer du
numéro de port de l’adresse IP du socket d’écoute qui a accepté la connexion.
4 La demande de connexion est acceptée par le serveur et gérée par le socket
client.
5 Un événement OnConnect se produit après l’établissement de la connexion. Si
votre socket doit immédiatement lancer la lecture ou l’écriture sur la
connexion, créez un gestionnaire d’événement OnConnect.

Utilisation des sockets 30-9


Réponse aux événements socket

Evénements serveur
Les composants socket serveur forment deux types de connexions : les
connexions d’écoute et les connexions aux applications client. Le socket serveur
reçoit des événements lors de la formation de ces deux types de connexions.

Evénements d’écoute
Juste avant que la connexion d’écoute soit formée, l’événement OnListen se
produit. Vous pouvez alors obtenir l’objet socket serveur Windows par le biais
de la propriété Socket. Vous pouvez utiliser sa propriété SocketHandle pour
modifier le socket avant qu’il ne soit ouvert pour l’écoute. Par exemple, si vous
souhaitez restreindre les adresses IP que le serveur utilise pour les écoutes, vous
pouvez le faire dans un gestionnaire d’événement OnListen.

Evénements de connexions client


Lorsqu’un socket serveur accepte une demande de connexion client, les
événements suivants se produisent :
1 Le socket serveur génère un événement OnGetSocket et transmet le handle de
socket Windows au socket qui forme l’extrémité serveur de la connexion. Si
vous souhaitez fournir votre propre descendant de TServerClientWinSocket,
vous pouvez en créer un dans un gestionnaire d’événement OnGetSocket ;
celui-ci sera utilisé à la place de TServerClientWinSocket.
2 Un événement OnAccept se produit, et transmet le nouvel objet
TServerClientWinSocket au gestionnaire d’événement. Ceci est le premier point
lorsque vous pouvez utiliser les propriétés de TServerClientWinSocket pour
obtenir des informations sur l’extrémité serveur de la connexion à un client.
3 Si ServerType est à stThreadBlocking, un événement OnGetThread se produit. Si
vous souhaitez fournir votre propre descendant de TServerClientThread, vous
pouvez en créer un dans un gestionnaire d’événement OnGetThread ; celui-ci
sera utilisé à la place de TServerClientThread. Pour plus d’informations sur la
création de threads client serveur personnalisés, voir “Ecriture de threads
serveur” à la page 30-14.
4 Si ServerType est à stThreadBlocking, un événement OnThreadStart se produit
lorsque le thread lance l’exécution. Si vous souhaitez effectuer une
initialisation du thread, ou faire des appels d’API de socket Windows avant
que le thread ne commence à lire ou à écrire dans la connexion, utilisez le
gestionnaire d’événement OnThreadStart.
5 Le client termine la connexion et un événement OnClientConnect se produit. Si
vous utilisez un serveur non bloquant, vous pouvez alors commencer les
lectures et les écritures sur la connexion socket.

30-10 Guide du développeur


Lectures et écritures sur des connexions socket

Lectures et écritures sur des connexions socket


L’une des raisons pour lesquelles vous formez des connexions socket avec
d’autres machines est de pouvoir lire et écrire des informations par le biais de
ces connexions. Le type d’informations que vous lisez et écrivez, ainsi que le
moment auquel vous les lisez ou les écrivez, dépendent du service associé à la
connexion socket.
Les lectures et les écritures sur sockets peuvent se dérouler en mode asynchrone
pour ne pas bloquer l’exécution d’autre code dans votre application réseau. Ceci
s’appelle une connexion non bloquante. Vous pouvez également former des
connexions bloquantes, lors desquelles votre application attend la fin de la
lecture ou de l’écriture avant d’exécuter les instructions suivantes.

Connexions non bloquantes


Les connexions non bloquantes lisent et écrivent de façon asynchrone, de sorte
que le transfert de données ne bloque pas l'exécution de code dans votre
application réseau. Pour créer une connexion non bloquante
• sur les sockets client, attribuez à la propriété ClientType la valeur
ctNonBlocking.
• sur les sockets serveur, attribuez à la propriété ServerType la valeur
stNonBlocking.
Lorsque la connexion est non bloquante, la lecture et l’écriture d’événements
informent votre socket de la tentative de lecture et d'écriture d'informations par
le socket de l'autre extrémité de la connexion.

Lecture et écriture d’événements


Les sockets non bloquants génèrent des événements de lecture et d'écriture qui
informent votre socket de la nécessité de lire ou d'écrire via la connexion. Avec
les sockets client, vous pouvez répondre à ces notifications dans un gestionnaire
d’événement OnRead ou OnWrite. Avec les sockets serveur, vous pouvez
répondre à ces notifications dans un gestionnaire d’événement OnClientRead ou
OnClientWrite.
L’objet socket Windows associé à la connexion socket est transmis sous forme de
paramètre aux gestionnaires d’événements de lecture et d’écriture. Cet objet
socket Windows fournit de nombreuses méthodes vous permettant de lire ou
d’écrire sur une connexion.
Pour lire sur la connexion socket, utilisez les méthodes ReceiveBuf ou ReceiveText.
Avant d’utiliser ReceiveBuf, utilisez la méthode ReceiveLength pour avoir une
estimation du nombre d’octets que le socket à l’autre bout de la connexion est
prêt à émettre.

Utilisation des sockets 30-11


Lectures et écritures sur des connexions socket

Pour écrire sur la connexion socket, utilisez la méthode SendBuf, SendStream ou


SendText. Si vous n’avez plus besoin de la connexion socket après avoir écrit vos
informations, vous pouvez utiliser la méthode SendStreamThenDrop.
SendStreamThenDrop ferme la connexion socket après réception de toutes les
données émises. Si vous utilisez la méthode SendStream ou SendStreamThenDrop,
ne libérez pas l’objet flux. En effet, le socket le libère automatiquement lorsque la
connexion se ferme.
Remarque SendStreamThenDrop ferme la connexion serveur à un client précis, et non pas la
connexion d’écoute.

Connexions bloquantes
Lorsque la connexion est bloquante, votre socket doit initier la lecture ou
l’écriture sur la connexion, plutôt qu’attendre la notification émanant de la
connexion socket. Utilisez un socket bloquant lorsque votre côté de la connexion
décide du moment où doivent s’effectuer les lectures et les écritures.
Pour les sockets client, mettez la propriété ClientType à ctBlocking pour former
une connexion bloquante. En fonction de ce dont est capable votre application
client, il peut être recommandé de créer un nouveau thread d’exécution pour les
lectures ou les écritures, de telle sorte que votre application puisse continuer
l’exécution du code dans d’autres threads tout en attendant la fin des lectures ou
des écritures sur la connexion.
Pour les sockets serveur, mettez la propriété ServerType à stThreadBlocking pour
former une connexion bloquante. Etant donné que les connexions bloquantes
interrompent l’exécution de tout autre code lorsque le socket attend que des
informations soient écrites ou lues sur la connexion, les composants de socket
serveur génèrent toujours un nouveau thread d’exécution pour chaque connexion
client lorsque le ServerType est à stThreadBlocking.

Utilisation de threads avec des connexions bloquantes


Les sockets client ne génèrent pas automatiquement de nouveaux threads
lorsqu’ils lisent ou écrivent à l’aide d’une connexion bloquante. Si votre
application client n’a rien à faire en attendant que les informations soient lues ou
écrites, tout est pour le mieux. Mais si votre application comporte une interface
qui doit rester disponible pour l’utilisateur, il est conseillé de générer un thread
distinct pour les lectures et les écritures de données.
Lorsque les sockets serveur forment des connexions bloquantes, ils génèrent
toujours des threads distincts pour chaque connexion client pour qu’aucun client
ne doive attendre qu’un autre client ait fini de lire ou d’écrire des données sur la
connexion. Par défaut, les sockets serveur utilisent des objets TServerClientThread
pour implémenter le thread d’exécution de chaque connexion.
Les objets TServerClientThread simulent les événements OnClientRead et
OnClientWrite se produisant avec les connexions non bloquantes. Cependant, ces
événements se produisent sur le socket à l’écoute, ce qui n’est pas local au thread.
Si les demandes du client sont fréquentes, vous devez créer un descendant de
TServerClientThread qui propose des lectures et écritures gérant les threads.

30-12 Guide du développeur


Lectures et écritures sur des connexions socket

Utilisation de TWinSocketStream
Quand vous implémentez le thread d’une connexion bloquante, déterminez le
moment où le socket à l’autre extrémité de la connexion est prêt à lire ou écrire.
Les connexions bloquantes n’informent pas le socket du moment où il faut lire ou
écrire. Pour savoir si la connexion est prête, utilisez un objet TWinSocketStream.
TWinSocketStream dispose de méthodes permettant d’aider à coordonner la
chronologie des lectures et écritures. Appelez la méthode WaitForData pour
attendre jusqu’à ce que le socket à l’autre extrémité soit prêt à écrire.
Si vous lisez ou écrivez en utilisant TWinSocketStream, le flux fait un timeout si la
lecture ou l’écriture n’est pas terminée au bout d’un laps de temps spécifié.
Ainsi, l’application socket ne se bloque pas indéfiniment en essayant de lire ou
d’écrire via une connexion perdue.
Remarque Vous ne pouvez pas utiliser TWinSocketStream avec une connexion non
bloquante.

Ecriture de threads client


Pour écrire le thread de connexion client, vous devez définir un nouvel objet
thread en utilisant la boîte de dialogue Nouvel objet Thread. Pour davantage
d’informations, voir “Définition d’objets thread” à la page 8-2.
La méthode Execute du nouvel objet thread gère les détails de la lecture et de
l’écriture via la connexion de thread. Elle crée un objet TWinSocketStream et
l’utilise pour lire ou écrire. Par exemple :
procedure TMyClientThread.Execute;
var
TheStream: TWinSocketStream;
buffer: string;
begin
{ crée un objet TWinSocketStream pour la lecture et l’écriture }
TheStream := TWinSocketStream.Create(ClientSocket1.Socket, 60000);
try
{ lit et traite les commandes jusqu’à l’arrêt de la connexion ou du thread }
while (not Terminated) and (ClientSocket1.Active) do
begin
try
GetNextRequest(buffer); { GetNextRequest doit être une méthode adaptée aux threads }
{ envoyer la demande au serveur }
TheStream.Write(buffer, Length(buffer) + 1);
{ continuer la communication (c.a.d. lire une réponse du serveur) }
...
except
if not(ExceptObject is EAbort) then
Synchronize(HandleThreadException); { il faut écrire HandleThreadException }
end;
end;
finally
TheStream.free;
end;
end;

Utilisation des sockets 30-13


Lectures et écritures sur des connexions socket

Pour utiliser le thread, créez-le dans un gestionnaire d’événements OnConnect.


Pour davantage d’informations sur la création et l’exécution de threads, voir
“Exécution d’objets thread” à la page 8-11.

Ecriture de threads serveur


Les threads des connexions serveur sont des descendants de TServerClientThread.
De ce fait, il n’est pas possible d’utiliser la boîte de dialogue Nouvel objet
Thread. Il faut, à la place, déclarer le thread manuellement comme suit :
TMyServerThread = class(TServerClientThread);
Pour implémenter ce thread, vous redéfinissez la méthode ClientExecute au lieu
de la méthode Execute.
L’implémentation de la méthode ClientExecute est similaire à celle de la méthode
Execute du thread d’une connexion client. Cependant, au lieu d’utiliser un
composant socket client de la palette des composants qui est placé dans
l’application, le thread serveur doit utiliser l’objet TServerClientWinSocket créé
quand le socket serveur d’écoute accepte une connexion client. Cela est proposé
par la propriété publique ClientSocket. De plus, vous pouvez utiliser la méthode
protégée HandleException au lieu d’écrire votre propre gestion des exceptions de
thread. Par exemple :
procedure TMyServerThread.ClientExecute;
var
Stream : TWinSocketStream;
Buffer : array[0 .. 9] of Char;
begin
{ Vérifier que la connexion est active }
while (not Terminated) and ClientSocket.Connected do
begin
try
Stream := TWinSocketStream.Create(ClientSocket, 60000);
try
FillChar(Buffer, 10, 0); { initialise le tampon }
{ Donner 60 secondes au client pour commencer à écrire }
if Stream.WaitForData(60000) then
begin
if Stream.Read(Buffer, 10) = 0 then { Si on ne peut lire, au bout de 60 secondes }
ClientSocket.Close; { fermer la connexion }
{ Traiter ici la demande }
...
end
else
ClientSocket.Close; { si le client ne commence pas, fermer }
finally
Stream.Free;
end;
except
HandleException;
end;
end;
end;

30-14 Guide du développeur


Lectures et écritures sur des connexions socket

Attention Les sockets serveur placent dans un cache les threads qu’ils utilisent. Assurez-
vous que la méthode ClientExecute effectue les initialisations nécessaires afin qu’il
n’y ait pas de problème quand le thread s’exécute pour la dernière fois.
Pour utiliser votre thread, créez-le dans un gestionnaire d’événements
OnGetThread. Lors de la création du thread, affectez la valeur False au paramètre
CreateSuspended.

Utilisation des sockets 30-15


30-16 Guide du développeur
Partie

IV
Création de composants
Part IV

personnalisés
Les chapitres de cette partie présentent les concepts nécessaires à la conception et
à l’implémentation de composants personnalisés dans Delphi.

Création de composants personnalisés


Chapitre

Présentation générale de la création


Chapter 31
31
d’un composant
Ce chapitre est une présentation générale de la conception des composants et du
processus d’écriture des composants pour les applications Delphi. Vous devez
toutefois être familier de Delphi et de ses composants standard.
• La bibliothèque des composants visuels
• Composants et classes
• Comment créer un composant ?
• Contenu d’un composant ?
• Création d’un nouveau composant
• Test des composants non installés
Pour des informations sur l’installation de nouveaux composants, voir
“Installation de paquets de composants” à la page 9-6.

La bibliothèque des composants visuels


Les composants de Delphi sont intégrés à une hiérarchie de classes appelée
Bibliothèque des composants visuels (Visual Component Library  VCL). La
figure 31.1 montre la relation qui existe entre les classes sélectionnées qui
composent la VCL. Pour plus de détails sur les hiérarchies de classes et les
relations d’héritage entre classes, voir chapitre 32, “Programmation orientée objet
et écriture des composants”.
La classe TComponent est l’ancêtre partagé de chaque composant de la VCL.
TComponent fournit les propriétés et les événements de base nécessaires au
fonctionnement d’un composant dans Delphi. Les différentes branches de la
bibliothèque offrent d’autres possibilités plus spécialisées.

Présentation générale de la création d’un composant 31-1


Composants et classes

Figure 31.1 Hiérarchie des classes de la bibliothèque de composants visuels

Lorsque vous créez un composant, vous l’ajoutez à la VCL en dérivant une


nouvelle classe de l’un des types de classes existant dans la hiérarchie.

Composants et classes
Comme les composants sont des classes, les créateurs de composants manipulent
les objets à un niveau différent de celui des développeurs d’applications. La
création de nouveaux composants nécessite de dériver de nouvelles classes.
Brièvement, il existe deux différences principales entre la création des composants
et leur utilisation dans des applications. Pour la création de composants,
• Vous avez accès à des parties de la classe qui sont inaccessibles aux
programmeurs d’applications.
• Vous ajoutez de nouvelles parties (des propriétés, par exemple) aux composants.
A cause de ces différences, il faut prendre en compte un plus grand nombre de
conventions, et réfléchir à la manière dont les développeurs d’applications vont
utiliser les composants.

Comment créer un composant ?


N’importe quel élément de programme manipulé lors de la phase de conception
peut constituer un composant. La création d’un composant consiste à dériver une
nouvelle classe à partir d’une classe existante. Vous pouvez dériver un nouveau
composant de n’importe quel composant existant, mais les méthodes les plus
courantes pour créer des composants sont les suivantes :
• Modification de contrôles existants

31-2 Guide du développeur


Comment créer un composant ?

• Création de contrôles fenêtrés


• Création de contrôles graphiques
• Sous-classement de contrôles Windows
• Création de composants non visuels
Le tableau suivant présente les différents types de composants et les classes que
vous utiliserez comme point de départ pour chacun d’eux.

Tableau 31.1 Points de départ de la création de composants


Pour Démarrez avec le type suivant
Modifier un composant existant N’importe quel composant existant tel que TButton ou
TListBox, ou un type de composant abstrait tel que
TCustomListBox
Créer un contrôle fenêtré TWinControl
Créer un contrôle graphique TGraphicControl
Sous-classer un contrôle Windows Tout contrôle Windows
Créer un composant non visuel TComponent

Vous pouvez aussi dériver des classes qui ne sont pas des composants et ne
peuvent pas être manipulées dans une fiche. Delphi inclut de nombreuses classes
de ce type, telles que TRegIniFile et TFont.

Modification de contrôles existants


Le moyen le plus simple de créer un composant est de modifier un composant
existant. Vous pouvez dériver un nouveau composant depuis un composant
quelconque de Delphi.
Certains contrôles, tels les boîtes liste et les grilles, possèdent plusieurs variantes
d’un thème de base. Dans ce cas, la VCL comprend un type de classe abstraite
(son nom contient le mot “custom”, comme TCustomGrid) à partir de laquelle il
est possible de dériver les versions personnalisées.
Vous pouvez, par exemple, créer un type particulier de boîte liste ne possédant pas
certaines propriétés de la classe TListBox. Comme il n’est pas possible de retirer
(masquer) une propriété héritée d’une classe ancêtre, il faut dériver le composant
d’un élément situé avant TListBox dans la hiérarchie. Au lieu de vous forcer à
commencer depuis la classe abstraite TWinControl et à réinventer toutes les
fonctions de boîte liste, la bibliothèque des composants visuels (Visual Component
Library Reference) fournit TCustomListBox, qui implémente toutes les propriétés des
boîtes liste mais ne les rend pas toutes publiques. En dérivant un composant à
partir de l’une des classes abstraites telles que TCustomListBox, vous rendez
publiques uniquement les propriétés que vous souhaitez mettre à disposition dans
votre composant et vous laissez les autres protégées.
Le chapitre 33, “Création de propriétés”, explique la publication des propriétés
héritées. Le chapitre 39, “Modification d’un composant existant”, et le chapitre 41,
“Personnalisation d’une grille”, montrent des exemples de modification de
contrôles existants.

Présentation générale de la création d’un composant 31-3


Comment créer un composant ?

Création de contrôles fenêtrés


Les contrôles fenêtrés sont des objets qui apparaissent à l’exécution et avec
lesquels l’utilisateur peut interagir. Chaque contrôle fenêtré possède un handle
de fenêtre, accessible via sa propriété Handle, qui permet à Windows de
l’identifier et d’agir sur lui. Le handle permet au contrôle de recevoir la
focalisation de saisie et peut être transmis aux fonctions de l’API Windows.
Tous les contrôles fenêtrés descendent de la classe TWinControl. Ils incluent la
plupart de contrôles standard de Windows, tels les boutons poussoirs, les boîtes
liste et les boîtes de saisie. Bien que vous puissiez créer un contrôle original (un
qui n’est relié à aucun contrôle existant) en le dérivant directement de
TWinControl, Delphi fournit pour cela le composant TCustomControl.
TCustomControl est un contrôle fenêtré spécialisé qui permet de réaliser
facilement des images visuelles complexes.
Le chapitre 41, “Personnalisation d’une grille”, présente un exemple de création
d’un contrôle fenêtré.

Création de contrôles graphiques


Si votre contrôle n’a pas besoin de recevoir la focalisation de saisie, vous pouvez
en faire un contrôle graphique. Les contrôles graphiques sont semblables aux
contrôles fenêtrés, mais ils ne possèdent pas de handle de fenêtre et consomment
donc moins de ressources système. Les composants comme TLabel, qui ne
reçoivent jamais la focalisation de saisie, sont des contrôles graphiques. Bien que
ces contrôles ne puissent pas recevoir la focalisation, vous pouvez les créer afin
qu’ils réagissent aux messages souris.
Delphi supporte la création de contrôles personnalisés par l’intermédiaire du
composant TGraphicControl. TGraphicControl est une classe abstraite dérivée de
TControl. Bien qu’il soit possible de dériver des contrôles directement de TControl,
il est préférable de les dériver de TGraphicControl, qui procure un canevas de
dessin et gère les messages WM_PAINT ; il vous suffit de surcharger la méthode
Paint.
Le chapitre 40, “Création d’un composant graphique”, montre un exemple de
création d’un contrôle graphique.

Sous-classement de contrôles Windows


En programmation Windows traditionnelle, vous créez des contrôles
personnalisés en définissant une nouvelle classe fenêtre et en l’enregistrant dans
Windows. La classe fenêtre (semblable aux objets ou aux classes dans la
programmation orientée objet). Vous pouvez baser une nouvelle classe fenêtre
sur une classe existante : cette opération est appelée sous-classement. Vous pouvez
ensuite placer votre contrôle dans une bibliothèque dynamiquement liée (DLL),
comme les contrôles Windows standard, puis lui fournir une interface.

31-4 Guide du développeur


Contenu d’un composant ?

Avec Delphi vous pouvez créer une “enveloppe” de composant autour de


n’importe quelle classe fenêtre existante. Ainsi, si vous possédez déjà une
bibliothèque de contrôles personnalisés que vous souhaitez utiliser dans vos
applications Delphi, vous pouvez créer des composants Delphi se comportant
comme ces contrôles et dériver de nouveaux contrôles à partir d’eux, comme
vous le feriez avec n’importe quel composant.
Pour avoir des exemples de sous-classement des contrôles Windows, examinez
l’unité STDCTLS qui contient les contrôles Windows standard, comme TEdit.

Création de composants non visuels


Les composants non visuels sont utilisés en tant qu’interfaces pour des éléments
comme les bases de données (TDataSet) et les horloges système (TTimer), et en
tant que marques de réservation pour des boîtes de dialogue (TCommonDialog et
ses descendants). La plupart des composants que vous écrivez sont des contrôles
visuels. les composants non visuels peuvent être dérivés directement de
TComponent, la classe abstraite de base de tous les composants.

Contenu d’un composant ?


Pour que vos composants s’intègrent de manière sûre à l’environnement de Delphi,
vous devez suivre certaines conventions. Cette section traite des sujets suivants :
• Suppression des dépendances
• Propriétés, méthodes et événements
• Encapsulation des graphiques
• Recensement

Suppression des dépendances


Une des qualités qui favorisent l’utilisation des composants est le caractère
illimité des opérations que l’on peut programmer dans leur code. Par nature, les
composants peuvent être incorporés aux applications avec diverses combinaisons,
dans des ordres ou des contextes différents. Les composants doivent être conçus
pour pouvoir fonctionner dans n’importe quelle situation, sans condition préalable.
La propriété Handle des composants TWinControl constitue un excellent exemple
de suppression des dépendances dans les composants. Si vous avez déjà écrit des
applications Windows, vous savez que l’un des points les plus complexes à traiter
et les plus susceptibles de générer des erreurs lors de l’exécution d’un programme
est l’interdiction d’accéder à une fenêtre ou à un contrôle avant de l’avoir créé par
un appel à la fonction API CreateWindow. Les contrôles fenêtrés de Delphi évitent
cette difficulté en garantissant qu’un handle de fenêtre correct sera toujours
disponible dès que nécessaire. En utilisant une propriété pour représenter le
handle de fenêtre, le contrôle peut vérifier si la fenêtre a été créée ; si le handle
n’est pas correct, le contrôle crée une fenêtre et renvoie son handle. Ainsi,
chaque fois que le code d’une application accède à la propriété Handle, il est sûr
d’obtenir un handle correct.

Présentation générale de la création d’un composant 31-5


Contenu d’un composant ?

En les libérant des tâches d’arrière-plan telles que la création des fenêtres, les
composants Delphi permettent aux développeurs de se concentrer sur ce qu’ils
veulent vraiment réaliser. Pour transmettre un handle de fenêtre à une fonction
API, il n’est pas nécessaire de vérifier que le handle existe ni de créer la fenêtre.
Le programmeur est certain que les opérations vont se dérouler correctement et
n’a pas besoin de le contrôler sans cesse.
Bien que la création de composants sans dépendances soit un peu plus longue, le
temps qui y est consacré est généralement très utile. Non seulement cela évite aux
développeurs répétitions et travail fastidieux, mais cela réduit la quantité de
documentation et de support.

Propriétés, méthodes et événements


En dehors de l’image visible que l’utilisateur du composant manipule dans le
concepteur de fiches, les attributs les plus courants d’un composant sont les
propriétés, les événements et les méthodes. Un chapitre est consacré à chacun
d’eux, mais ce qui suit présente certaines raisons de les utiliser.

Propriétés
Les propriétés donnent au développeur d’applications l’illusion de définir ou de
lire la valeur d’une variable, tout en permettant au concepteur de composants de
dissimuler la structure de données sous-jacente ou de définir un traitement
spécial lorsque la valeur est accédée.
L’utilisation des propriétés présente plusieurs avantages :
• Les propriétés sont disponibles au moment de la conception. Le développeur
d’applications peut définir ou modifier les valeurs initiales des propriétés sans
écrire de code.
• Les propriétés peuvent contrôler les valeurs ou les formats au moment où le
développeur les définit. La validation de la saisie pendant la conception empêche
de commettre des erreurs.
• Le composant peut construire les valeurs appropriées à la demande. L’erreur de
programmation la plus fréquente est de référencer une variable qui n’a été
initialisée. En représentant les données par une propriété, vous êtes sûr qu’une
valeur leur est toujours disponible sur demande.
• Les propriétés vous permettent de cacher les données sous une interface
simple et cohérente. Vous pouvez modifier la façon dont les informations sont
structurées dans une propriété sans que ce changement ne soit perçu par les
développeurs d’applications.
Le chapitre 33, “Création de propriétés”, explique comment ajouter des propriétés
à vos composants.

31-6 Guide du développeur


Contenu d’un composant ?

Evénements
Un événement est une propriété spéciale qui appelle du code, pendant
l’exécution, en réponse à une saisie ou à une autre opération. Les événements
permettent aux développeurs d’associer des blocs de code spécifiques à des
actions spécifiques, telles des manipulations de la souris ou des frappes au
clavier. Le code qui s’exécute lorsqu’un événement survient est appelé le
gestionnaire de l’événement.
Les événements permettent aux développeurs d’applications de spécifier des
réponses différentes en fonction des actions possibles sans avoir à créer de
nouveaux composants.
Le chapitre 34, “Création d’événements”, explique comment implémenter des
événements standard et comment en définir de nouveaux.

Méthodes
Les méthodes de classes sont des fonctions et procédures qui opèrent sur une
classe plutôt que sur des instances particulières de cette classe. Par exemple, les
méthodes constructeur de composants (Create) sont toutes des méthodes de
classes. Les méthodes de composants sont des procédures et fonctions qui
opèrent sur les instances des composants elles-mêmes. Les développeurs
d’applications utilisent des méthodes pour que les composants effectuent des
actions particulières ou renvoient des valeurs non contenues par des propriétés.
Comme elles nécessitent une exécution de code, les méthodes ne sont disponibles
qu’au moment de l’exécution. Elles sont utiles pour plusieurs raisons :
• Les méthodes encapsulent la fonctionnalité d’un composant dans l’objet même
où résident les données.
• Les méthodes peuvent cacher des procédures compliquées sous une interface
simple et cohérente. Un développeur d’applications peut appeler la méthode
AlignControls d’un composant sans savoir comment elle fonctionne ni si elle
diffère de la méthode AlignControls d’un autre composant.
• Les méthodes permettent de mettre à jour plusieurs propriétés avec un seul
appel.
Le chapitre 35, “Création de méthodes”, explique comment ajouter des méthodes
à vos composants.

Encapsulation des graphiques


Delphi simplifie les graphiques Windows en encapsulant les différents outils
graphiques dans un canevas. Le canevas représente la surface de dessin d’une
fenêtre ou d’un contrôle ; il contient d’autres classes telles qu’un crayon, un pinceau
et une police de caractères. Un canevas est semblable à un contexte de périphérique
Windows, mais il prend à sa charge toutes les opérations de gestion.

Présentation générale de la création d’un composant 31-7


Création d’un nouveau composant

Si vous avez déjà écrit une application Windows graphique, vous connaissez les
contraintes imposées par l’interface graphique Windows (GDI), comme les limites
sur le nombre de contextes de périphériques disponibles et l’obligation de restaurer
l’état initial des objets graphiques avant de les détruire.
Avec Delphi, vous n’avez pas besoin de vous en préoccuper. Pour dessiner sur
une fiche ou tout autre composant, il suffit d’accéder à la propriété Canvas du
composant. Pour personnaliser un crayon ou un pinceau, il faut définir une couleur
ou un style. Lorsque vous avez terminé, Delphi dispose des ressources. Delphi
conserve les ressources en mémoire cache pour éviter de les recréer, si votre
application utilise fréquemment le même type de ressources.
Vous pouvez toujours accéder à l’interface GDI Windows, mais votre code sera
beaucoup plus simple et s’exécutera plus rapidement si vous utilisez le canevas
intégré aux composants Delphi. Les fonctionnalités graphiques sont décrites en
détail chapitre 36, “Graphiques et composants”.

Recensement
Avant de pouvoir installer vos composants dans l’EDI de Delphi, vous devez les
recenser. Le recensement indique à Delphi où le composant doit apparaître sur la
palette des composants. Vous pouvez aussi personnaliser la manière dont Delphi
stocke les composants dans le fichier fiche. Le recensement est décrit chapitre 38,
“Accessibilité des composants au moment de la conception”.

Création d’un nouveau composant


Vous pouvez créer un nouveau composant de deux façons :
• Utilisation de l’expert composant
• Création manuelle d’un composant
Vous pouvez utiliser l’une ou l’autre de ces méthodes pour créer un composant
aux fonctions minimales, prêt à être installé dans la palette des composants.
Après l’installation, vous pouvez placer votre nouveau composant sur une fiche
et le tester à la fois en mode conception et en mode exécution. Vous pouvez
ensuite ajouter d’autres fonctionnalités au composant, mettre à jour la palette des
composants et poursuivre les tests.
Il y a quelques étapes de base à suivre chaque fois que vous créez un nouveau
composant. Elles sont décrites ci-après ; pour les autres exemples présentés dans
ce document, nous supposerons que vous savez effectuer ces étapes.
1 Création d’une unité pour le nouveau composant.
2 Dérivation du composant à partir d’un type de composant existant.
3 Ajout de propriétés, méthodes et événements.

31-8 Guide du développeur


Création d’un nouveau composant

4 Recensement de votre composant dans Delphi.


5 Création d’un fichier d’aide pour le composant et ses propriétés, méthodes et
événements.
6 Création d’un paquet (bibliothèque dynamiquement liée spéciale) pour
pouvoir installer le composant dans l’EDI de Delphi.
Lorsque vous avez terminé, le composant complet est constitué des fichiers
suivants :
• Un fichier paquet (.BPL) ou un fichier collection de paquets (.DPC)
• Un fichier paquet compilé (.DCP)
• Un fichier unité compilée (.DCU)
• Un fichier bitmap pour la palette (.DCR)
• Un fichier d’aide (.HLP)
La création d’un fichier d’aide à l’attention des utilisateurs de composants est
facultative. L’inclusion d’un fichier d’aide n’est requise que lorsqu’il est créé.
Les chapitres du reste de la Partie IV expliquent tous les aspects de la
construction des composants et offrent des exemples complets d’écriture de
plusieurs sortes de composants.

Utilisation de l’expert composant


L’expert composant simplifie les premières étapes de création d’un composant.
Lorsque vous utilisez l’expert composant, il suffit de spécifier :
• La classe à partir de laquelle le composant est dérivé
• Le nom de classe du nouveau composant
• La page de la palette des composants dans laquelle vous voulez qu’il apparaisse
• Le nom de l’unité dans laquelle le composant est créé
• Le chemin d’accès à cette unité
• Le nom du paquet dans lequel vous voulez placer le composant
L’expert composant exécute les opérations que vous exécuteriez pour créer
manuellement un composant :
• Création d’une unité
• Dérivation du composant.
• Recensement du composant.
L’expert composant ne peut pas ajouter de composant à une unité existante. Cela
ne peut se faire que manuellement.
Pour ouvrir l’expert composant, choisissez l’une de ces deux méthodes :
• Choisissez Composant|Nouveau composant.
• Choisissez Fichier|Nouveau et double-cliquez sur Composant

Présentation générale de la création d’un composant 31-9


Création d’un nouveau composant

Figure 31.2 L’expert composant

Remplissez les champs de l’expert composant :


1 Dans Type ancêtre, spécifiez la classe à partir de laquelle vous dérivez le
nouveau composant.
2 Dans Nom de classe, spécifiez le nom de classe de votre nouveau composant.
3 Dans Page de palette, spécifiez la page de la palette dans laquelle vous voulez
installer le composant.
4 Dans Nom de fichier unité, spécifiez le nom de l’unité dans laquelle vous
voulez déclarer la classe du composant.
5 Si l’unité n’est pas dans le chemin de recherche, modifiez ce dernier.
Pour placer un composant dans un paquet nouveau ou non, cliquez sur
Composant|Installer et spécifiez le paquet dans la boîte de dialogue qui apparaît.
Attention Si vous dérivez un composant d’une classe de la VCL dont le nom commence
par “custom” (comme TCustomControl), ne tentez pas de le placer sur une fiche
avant d’avoir surchargé toute méthode abstraite du composant initial. Delphi ne
peut pas créer d’instance d’une classe ayant des propriétés ou des méthodes
abstraites.
Pour voir le code source de votre unité, cliquez sur Voir l’unité (si l’expert
composant est déjà fermé, ouvrez le fichier unité dans l’éditeur de code en
sélectionnant Fichier|Ouvrir). Delphi crée une nouvelle unité contenant la
déclaration de classe et la procédure Register, et ajoute une clause uses qui
comprend toutes les unités Delphi standard. L’unité ressemble à cela :
unit MyControl;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
TMyControl = class(TCustomControl)
private
{ déclarations privées }

31-10 Guide du développeur


Création d’un nouveau composant

protected
{ déclarations protégées }
public
{ déclarations publiques }
published
{ déclarations publiées }
end;
procedure Register ;
implementation
procedure Register ;
begin
RegisterComponents('Samples', [TMyControl]);
end;
end.

Création manuelle d’un composant


Le moyen le plus simple de créer un composant est d’utiliser l’expert composant.
Cependant, vous pouvez effectuer manuellement les mêmes étapes.
Pour créer un composant manuellement, suivez ces étapes :
1 Création d’un fichier unité
2 Dérivation du composant
3 Recensement du composant

Création d’un fichier unité


Une unité est un module de code Pascal Objet compilé séparèment. Delphi
emploie les unités pour plusieurs raisons. Chaque fiche possède sa propre unité
et la plupart des composants (ou des groupes logiques de composants) possèdent
aussi leurs propres unités
Lorsque vous créez un composant, vous créez une nouvelle unité pour ce
composant ou bien vous l’ajoutez à une unité existante.
Pour créer une unité, choisissez Fichier|Nouveau et double-cliquez sur Unité.
Delphi crée un nouveau fichier unité et l’ouvre dans l’éditeur de code.
Pour ouvrir une unité existante, choisissez Fichier|Ouvrir et sélectionnez l’unité
de code source dans laquelle vous voulez ajouter vos composants.
Remarque Lorsque vous ajoutez un composant à une unité, vérifiez que cette unité ne
contient que du code de composant. L’ajout d’un code de composant à une unité
qui contient, par exemple, une fiche, provoquera des erreurs dans la palette des
composants.
Une fois l’unité, nouvelle ou existante, définie pour le composant, vous pouvez
dériver la classe composant.

Présentation générale de la création d’un composant 31-11


Création d’un nouveau composant

Dérivation du composant
Chaque composant est une classe dérivée de TComponent, de l’un de ses
descendants plus spécialisés (tels que TControl ou TGraphicControl) ou d’une
classe composant existante. “Comment créer un composant ?” à la page 31-2
indique les classes à dériver pour obtenir les différentes sortes de composants.
La dérivation des classes est expliquée plus en détail dans la section “Définition
de nouvelles classes” à la page 32-2.
Pour dériver un composant, ajoutez une déclaration de type objet à la partie
interface de l’unité qui contiendra le composant.
Une classe composant simple est un composant non visuel descendant
directement de TComponent.
Pour créer une classe composant simple, ajoutez la déclaration de classe suivante
à la partie interface de votre unité composant :
type
TNewComponent = class(TComponent)
end;
Pour l’instant, le nouveau composant ne fait rien de plus que TComponent. C’est
juste un squelette sur lequel vous allez bâtir votre nouveau composant.

Recensement du composant
Le recensement est une opération simple qui indique à Delphi les composants à
ajouter à la bibliothèque des composants et les pages de la palette sur lesquelles
ils doivent apparaître. Pour une présentation plus détaillée du processus de
recensement, voir chapitre 38, “Accessibilité des composants au moment de la
conception”.
Pour recenser un composant,
1 Ajoutez une procédure nommée Register à la partie interface de l’unité du
composant. Register n’a pas de paramètres, la déclaration est donc très simple :
procedure Register;
Si vous ajoutez un composant à une unité qui contient déjà des composants,
elle doit déjà avoir la procédure Register déclarée, afin que vous n’ayiez pas à
changer la déclaration.
2 Ecrivez la procédureRegister dans la partie implémentation de l’unité, en
appelant RegisterComponents pour chaque composant que vous voulez recenser.
RegisterComponents est une procédure qui prend deux paramètres : le nom
d’une page de palette de composants et un ensemble de types de composants.
Si vous ajoutez un composant à un recensement existant, vous pouvez soit
ajouter le nouveau composant à l’ensemble dans l’instruction existante, soit
ajouter une nouvelle instruction qui appelle RegisterComponents.

31-12 Guide du développeur


Test des composants non installés

Pour recenser un composant appelé TMyControl et le placer sur la page Exemples


de la palette, vous devrez ajouter la procédure Register suivante à l’unité
contenant la déclaration de TMyControl :
procedure Register;
begin
RegisterComponents('Samples', [TNewControl]);
end;
Cette procédure Register place TMyControl sur la page Exemples de la palette des
composants.
Une fois le composant recensé, vous pouvez le compiler dans un paquet (voir le
chapitre 20) et l’installer sur la palette des composants.

Test des composants non installés


Vous pouvez tester le comportement d’un composant à l’exécution avant de
l’installer sur la palette des composants. Cette technique est particulièrement utile
pour le débogage des composants nouvellement créés, mais vous pouvez l’utiliser
pour tester n’importe quel composant, que celui-ci apparaisse ou non sur la palette
des composants.
Vous pouvez tester un composant non installé en émulant les actions exécutées par
Delphi quand le composant est sélectionné dans la palette et placé dans une
fiche.
Pour tester un composant non installé,
1 Ajoutez le nom de l’unité du composant à la clause uses de l’unité fiche.
2 Ajoutez un champ objet à la fiche pour représenter le composant.
Il s’agit là d’une des différences principales entre votre façon d’ajouter des
composants et celle utilisée par Delphi. Vous ajoutez le champ objet à la partie
publique à la fin de la déclaration de type de fiche. Delphi l’ajouterait au-
dessus, dans la partie de la déclaration de type qu’il gère.
Il ne faut jamais ajouter de champs à la partie gérée par Delphi de la
déclaration de type de fiche. Les éléments de cette partie correspondent à ceux
stockés dans le fichier fiche. L’ajout de noms de composants qui n’existent pas
sur la fiche peut rendre invalide le fichier de la fiche.
3 Attachez un gestionnaire à l’événement OnCreate de la fiche.
4 Construisez le composant dans le gestionnaire OnCreate de la fiche.
Lors de l’appel au constructeur du composant, vous devez transmettre un
paramètre spécifiant le propriétaire du composant (le composant chargé de la
destruction du composant au moment opportun). Il faut pratiquement toujours
transmettre Self comme propriétaire. Dans une méthode, Self représente une
référence sur l’objet contenant la méthode. Dans ce cas, dans le gestionnaire
OnCreate de la fiche, Self représente la fiche.

Présentation générale de la création d’un composant 31-13


Test des composants non installés

5 Initialisez la propriété Parent.


La définition de la propriété Parent est toujours la première opération à
effectuer après la construction d’un contrôle. Le parent est le composant qui
contient visuellement le contrôle ; le plus souvent c’est sur la fiche que le
contrôle apparaît, mais il peut aussi s’agir d’une boîte groupe ou d’un volet.
Normalement, il faut donner à Parent la valeur Self, c’est-à dire la fiche.
Définissez toujours Parent avant les autres propriétés du contrôle.
Attention Si votre composant n’est pas un contrôle (c’est-à-dire si TControl n’est pas un
de ses ancêtres), passez cette étape. Si vous définissez accidentellement la
propriété Parent de la fiche (à la place de celle du composant) à la valeur Self,
vous pouvez provoquer un problème du système d’exploitation.
6 Définissez toutes les autres propriétés de composant, comme vous le
souhaitez.
Supposons que vous souhaitiez tester un nouveau composant de type
TMyControl dans une unité appelée MyControl. Créez un nouveau projet, puis
effectuez les étapes nécessaires pour avoir une unité fiche qui ressemble à ceci :
unit Unit1;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, MyControl; { 1. Ajouter NewTest à la clause uses }
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject); { 3. Attacher un gestionnaire à OnCreate }
private
{ Private declarations }
public
{ Public Declarations }
MyControl1: TMyControl1; { 2. Ajouter un champ objet }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
MyControl1 := TMyControl.Create(Self); { 4. Construire le composant }
MyControl1.Parent := Self; { 5. Définir la propriété Parent si le composant est
// un contrôle }
MyControl1.Left := 12; { 6. Définir d’autres propriétés... )
ƒ ...continuer si nécessaire}
end;
end.

31-14 Guide du développeur


Chapitre

Programmation orientée objet


Chapter 32
32
et écriture des composants
Si vous avez écrit des applications avec Delphi, vous savez déjà qu’une classe
contient à la fois du texte et du code, et que les classes sont manipulables aussi
bien au moment de la conception qu’à l’exécution. C’est ainsi que vous êtes
devenu utilisateur de composants.
Lorsque vous créez de nouveaux composants, votre approche des classes n’est
pas celle du développeur d’applications standard. Vous essayez de cacher les
travaux internes du composant aux développeurs qui vont les utiliser. En
choisissant les ancêtres appropriés à vos composants, en concevant des interfaces
qui exposent seulement les propriétés et les méthodes dont les développeurs ont
besoin, en suivant les autres conseils de ce chapitre, vous pourrez créer des
composants réutilisables parfaitement maniables.
Avant de commencer à créer des composants, vous devez comprendre les sujets
suivants qui se rapportent à la programmation orientée objet (OOP) :
• Définition de nouvelles classes
• Ancêtres, descendants et hiérarchies des classes
• Contrôle des accès
• Répartition des méthodes
• Membres abstraits d’une classe
• Classes et pointeurs

Programmation orientée objet et écriture des composants 32-1


Définition de nouvelles classes

Définition de nouvelles classes


La différence entre un concepteur de composants et un développeur
d’applications est la suivante : le concepteur de composants crée de nouvelles
classes et le développeur d’applications manipule les instances de ces classes.
Une classe est d’abord un type. Comme programmeur, vous travaillez sans arrêt
avec les types et les instances, même si vous ne parlez pas en ces termes. Par
exemple, vous créez des variables d’un certain type, par exemple Integer. Les
classes sont habituellement plus complexes que de simples types de données,
mais elles fonctionnent de la même façon : en affectant différentes valeurs aux
instances d’un même type, vous effectuez différentes tâches.
Par exemple, il est courant de créer une fiche contenant deux boutons appelés
OK et Annuler. Chacun correspond à une instance de la classe TButton, mais, en
attribuant une valeur différente à leurs propriétés Caption et différents
gestionnaires à leurs événements OnClick, vous faites se comporter différemment
les deux instances.

Dérivation de nouvelles classes


Deux raisons peuvent vous amener à dériver une nouvelle classe :
• Modifier les valeurs par défaut d’une classe pour éviter les répétitions
• Ajout de nouvelles capacités à une classe
L’objectif est le même dans ces deux cas : créer des objets réutilisables. Si vous
concevez vos objets en ayant en tête leur réutilisation, vous gagnerez un temps
considérable. Attribuez à vos classes des valeurs par défaut exploitables mais
rendez-les personnalisables.

Modifier les valeurs par défaut d’une classe pour éviter les répétitions
Dans tout programme, les répétitions superflues sont à proscrire. Si vous vous
surprenez à répéter les mêmes lignes de code, vous serez sans doute amené à les
placer à part dans une sous-routine ou fonction, ou encore à construire une
bibliothèque de routines utilisables par un autre programme. Le même
raisonnement s’applique aux composants. Si vous modifiez fréquemment les
mêmes propriétés ou si vous appelez les mêmes méthodes, vous créerez sans
doute un nouveau composant qui effectue ces tâches par défaut.
Par exemple, supposons qu’à chaque création d’une nouvelle application, vous
ajoutez une boîte de dialogue accomplissant une fonction déterminée. Bien qu’il
soit simple de recréer à chaque fois cette boîte de dialogue, c’est superflu. Vous
pouvez concevoir la boîte de dialogue une fois pour toute, définir ses propriétés
puis installer le composant enveloppe associé dans la palette des composants. En
faisant du dialogue un composant réutilisable, non seulement vous éliminez une
tâche répétitive mais renforcez la standardisation et minimisez les erreurs qui
peuvent être occasionnées par chaque nouvelle création de la boîte de dialogue.
Le chapitre 39, “Modification d’un composant existant”, montre un exemple qui
modifie les propriétés par défaut d’un composant.

32-2 Guide du développeur


Définition de nouvelles classes

Remarque Si vous voulez ne modifier que les propriétés publiées d’un composant existant
ou enregistrer des gestionnaires d’événement spécifiques à un composant ou à
un groupe de composants, vous pourrez accomplir ceci plus facilement en créant
un modèle de composant.

Ajout de nouvelles capacités à une classe


Une raison classique de créer de nouveaux composants est l’ajout de
fonctionnalités qui ne se trouvent pas dans les composants existants. Pour cela,
vous dérivez le nouveau composant à partir d’un composant existant ou à partir
d’une classe de base abstraite, comme TComponent ou TControl.
Dérivez votre nouveau composant à partir de la classe contenant le sous-
ensemble le plus proche des caractéristiques recherchées. Vous pouvez ajouter
des fonctionnalités à une classe, mais vous ne pouvez pas en soustraire. Par
conséquent, si une classe de composant contient des propriétés que vous ne
souhaitez pas inclure dans la vôtre, effectuez la dérivation à partir de l’ancêtre de
ce composant.
Par exemple, pour ajouter des fonctionnalités à une boîte liste, vous devez
dériver un nouveau composant à partir de TListBox. Mais, si vous souhaitez
ajouter de nouvelles fonctionnalités et exclure certaines de celles des boîtes liste
standard, il vous faut dériver votre boîte liste de l’ancêtre de TListBox,
TCustomListBox. Recréez (ou rendez visibles) les fonctionnalités de la boîte liste
que vous voulez, puis ajoutez vos propres fonctionnalités.
Le chapitre 41, “Personnalisation d’une grille” montre un exemple qui
personnalise une classe abstraite de composant.

Déclaration d’une nouvelle classe de composant


Outre les composants standard, Delphi fournit de nombreuses classes abstraites
qui serviront de base pour dériver de nouveaux composants. Le tableau 31.1 à la
page 31-3 montre les classes que vous pouvez utiliser pour créer vos propres
composants.
Pour déclarer une nouvelle classe de composant, ajoutez une déclaration de
classe au fichier unité du composant.
Voici la déclaration d’un composant graphique simple :
type
TSampleShape = class(TGraphicControl)
end;
Une déclaration de composant achevée comprend généralement la déclaration
des propriétés, des événements et des méthodes avant le end. Mais une
déclaration comme celle ci-dessus est aussi admise et représente un point de
départ pour l’ajout de fonctionnalités à votre composant..

Programmation orientée objet et écriture des composants 32-3


Ancêtres, descendants et hiérarchies des classes

Ancêtres, descendants et hiérarchies des classes


Les développeurs d’applications peuvent se servir du fait que chaque contrôle a
des propriétés Top et Left qui déterminent son emplacement sur la fiche. Pour
eux, il importe peu que tous les contrôles aient hérité ces propriétés d’un ancêtre
commun, TControl. Mais, lorsque vous écrivez un composant, vous devez savoir
à partir de quelle classe vous le dérivez de façon à ce qu’il reçoive en héritage
les éléments que vous souhaitez. Vous devez également connaître toutes les
fonctionnalités dont hérite votre composant de manière à les exploiter sans avoir
à les recréer vous-même.
La classe à partir de laquelle vous effectuez la dérivation est l’ancêtre immédiat.
Chaque composant hérite de son ancêtre immédiat, et lui-même de son ancêtre
immédiat. Toutes les classes dont hérite un composant sont les ancêtres de ce
composant ; le composant est un descendant de ces ancêtres.
L’ensemble des relations ancêtre-descendant de l’application constitue les
hiérarchies des classes. Dans cette hiérarchie, chaque génération contient plus
que ses ancêtres puisqu’une classe hérite de tout ce que contiennent ses ancêtres
et ajoute de nouvelles propriétés et de nouvelles méthodes, ou redéfinit celles
qui existent.
Si vous ne spécifiez aucun ancêtre immédiat, Delphi dérive votre composant à
partir de l’ancêtre par défaut, TObject. TObject est le dernier ancêtre de toutes les
classes de la hirarchie d’objets.
La règle générale du choix de l’objet de départ de la dérivation est simple :
prenez l’objet qui contient le plus possible ce que vous voulez inclure dans votre
nouvel objet, mais qui ne comprend rien de ce que vous ne voulez pas dans le
nouvel objet. Vous pouvez toujours ajouter des choses à vos objets, mais vous ne
pouvez pas en retirer.

Contrôle des accès


Il existe cinq niveaux de contrôle d’accès, également appelé visibilité, aux
propriétés, méthodes et champs. La visibilité détermine quel code peut accéder à
quelles parties de la classe. En spécifiant la visibilité, vous définissez l’interface de
vos composants.
Le tableau 32.1, “Niveaux de visibilité d’un objet,” montre les niveaux de
visibilité, en allant du plus restrictif au plus accessible:

Tableau 32.1 Niveaux de visibilité d’un objet


Visibilité Signification Utilisé pour
private Accessible uniquement au code de Masquer les détails d’implémentation.
l’unité où est définie la classe.
protected Accessible au code de ou des Définition de l’interface avec le
unités où sont définis la classe et concepteur des composants.
ses descendants.

32-4 Guide du développeur


Contrôle des accès

Tableau 32.1 Niveaux de visibilité d’un objet (suite)


Visibilité Signification Utilisé pour
public Accessible à tout le code. Définition de l’interface d’exécution.
automated Accessible à tout le code. Les Automatisation OLE seulement.
informations d’automatisation sont
générées.
published Accessible à tout le code et depuis Définition de l’interface de conception.
l’inspecteur d’objets.

Déclarez les membres en private si vous voulez qu’ils ne soient disponibles que
dans la classe où ils ont été définis. Déclarez-les en protected si vous voulez
qu’ils ne soient disponibles que dans cette classe et ses descendants. Souvenez-
vous que si un membre est disponible n’importe où dans un fichier unité, il est
disponible partout dans ce fichier. Ainsi, si vous définissez deux classes dans la
même unité, elles pourront accéder à l’une ou l’autre des méthodes privées. Et si
vous dérivez une classe dans une unité différente de son ancêtre, toutes les
classes de la nouvelle unité pourront accéder aux méthodes protégées de
l’ancêtre.

Masquer les détails d’implémentation


Déclarer private une partie d’une classe rend cette partie invisible au code situé
hors du fichier unité de la classe. Dans l’unité qui contient la déclaration, le code
peut accéder à cette partie comme si elle était publique.
Voici un exemple qui montre comment le fait de déclarer une donnée membre
private empêche les utilisateurs d’accéder aux informations. La liste montre les
deux unités de fiche. Chaque fiche a un gestionnaire pour son événement OnCreate
qui affecte une valeur à la donnée membre private. Le compilateur autorise
l’affectation à la donnée membre uniquement dans la fiche où elle a été déclarée.
unit HideInfo;
interface
uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs;
type
TSecretForm = class(TForm) { déclare une nouvelle fiche}
procedure FormCreate(Sender: TObject);
private { déclare la partie privée}
FSecretCode: Integer; { déclare une donnée membre private}
end;
var
SecretForm: TSecretForm;
implementation
procedure TSecretForm.FormCreate(Sender: TObject);
begin
FSecretCode := 42; { ceci se compile correctement}
end;
end. { fin de l’unité}

Programmation orientée objet et écriture des composants 32-5


Contrôle des accès

unit TestHide; { il s’agit du fichier fiche principal }


interface
uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs,
HideInfo; { utilise l’unité avec TSecretForm
}
type
TTestForm = class(TForm)
procedure FormCreate(Sender: TObject);
end;
var
TestForm: TTestForm;
implementation
procedure TTestForm.FormCreate(Sender: TObject);
begin
SecretForm.FSecretCode := 13; { le compilateur s’arrête avec
// "Identificateur de champ attendu" }
end;
end. { fin de l’unité}
Bien qu’un programme utilisant l’unité HideInfo puisse utiliser des objets de type
TSecretForm, il ne peut pas accéder à la donnée membre FSecretCode dans aucun
de ces objets.

Définition de l’interface avec le concepteur des composants


Déclarer protected une partie d’une classe rend cette partie uniquement visible
par cette classe et par ses descendants (et par les autres classes qui partagent
leurs fichiers unité).
Vous pouvez utiliser les déclarations protected pour définir l’interface de
conception des composants d’une classe. Les unités de l’application ne peuvent pas
accéder aux parties protected, mais les classes dérivées le peuvent. Cela signifie
que les concepteurs des composants peuvent modifier la façon dont fonctionne
une classe sans rendre apparents ces détails aux développeurs d’applications.

Définition de l’interface d’exécution


Déclarer public une partie d’une classe rend cette partie visible par tout code qui
dispose d’un accès global à cette classe.
Les parties publiques sont disponibles à l’ensemble du code lors de l’exécution,
et constituent par là-même l’interface d’exécution. L’interface d’exécution sert aux
éléments qui sont sans signification ou inappropriés au moment de la
conception ; comme les propriétés contenant des informations uniquement
disponibles à l’exécution ou accessibles uniquement en lecture. Les méthodes
destinées à être appelées par les développeurs d’applications doivent également
être publiques.

32-6 Guide du développeur


Contrôle des accès

Voici un exemple montrant deux propriétés accessibles uniquement en lecture et


déclarées comme faisant partie de l’interface d’exécution d’un composant :
type
TSampleComponent = class(TComponent)
private
FTempCelsius: Integer; { les détails de l’implémentation sont privés}
function GetTempFahrenheit: Integer;
public
property TempCelsius: Integer read FTempCelsius; { les propriétés sont publiques}
property TempFahrenheit: Integer read GetTempFahrenheit;
end;
ƒ
function TSampleComponent.GetTempFahrenheit: Integer;
begin
Result := FTempCelsius * 9 div 5 + 32;
end;

Définition de l’interface de conception


Déclarer published une partie d’une classe rend publique cette partie et génère
également les informations de types à l’exécution. Entre autres rôles, les
informations de types à l’exécution permettent à l’inspecteur d’objets d’accéder
aux propriétés et aux événements.
Parce qu’ils apparaissent dans l’inspecteur d’objets, les parties published d’une
classe définissent l’interface de conception de cette classe. L’interface de conception
doit inclure toutes les caractéristiques d’une classe qu’un développeur
d’applications peut vouloir personnaliser au moment de la conception, tout en
excluant toutes les propriétés qui dépendent d’une information spécifique issue
de l’environnement d’exécution.
Les propriétés accessibles en lecture uniquement ne peuvent pas faire partie de
l’interface de conception car le développeur d’applications ne peut pas leur
affecter des valeurs directement. Les propriétés accessibles en lecture uniquement
doivent donc être déclarées public plutôt que published.
Voici l’exemple d’une propriété published nommée Temperature. De ce fait, elle
apparaît dans l’inspecteur d’objets au moment de la conception.
type
TSampleComponent = class(TComponent)
private
FTemperature: Integer; { les détails d’implémentation sont privés }
published
property Temperature: Integer read FTemperature write FTemperature; { peut s’écrire ! }
end;

Programmation orientée objet et écriture des composants 32-7


Répartition des méthodes

Répartition des méthodes


Le terme de Dispatch fait référence à la façon dont un programme détermine où il
doit rechercher une méthode de classe lorsqu’il rencontre un appel à cette
méthode. Le code qui appelle une méthode de classe ressemble à tout autre appel
de fonction. Mais les classes ont des façons différentes de répartir les méthodes.
Les trois types de répartition de méthodes sont
• Statique
• Virtuel
• Dynamique

Méthodes statiques
Toutes les méthodes sont statiques à moins que vous ne les déclariez
spécifiquement autrement. Les méthodes statiques fonctionnent comme des
procédures ou des fonctions normales. Le compilateur détermine l’adresse exacte
de la méthode et la lie au moment de la compilation.
Le premier avantage des méthodes statiques est qu’elles sont réparties très
rapidement. Comme le compilateur peut déterminer l’adresse exacte de la
méthode, il la lie directement. Les méthodes virtuelles et dynamiques, au
contraire, utilisent des moyens indirects pour donner l’adresse de leurs méthodes
lors de l’exécution, ce qui prend davantage de temps.
Une méthode statique ne change pas lorsqu’elle est héritée d’une classe descendante.
Si vous déclarez une classe comprenant une méthode statique, puis dérivez une
nouvelle classe à partir de celle-ci, la classe dérivée partage exactement la même
méthode à la même adresse. Cela signifie que vous ne pouvez pas redéfinir des
méthodes statiques. Une méthode statique réalise toujours la même chose quelle que
soit la classe qui y est appelée. Si vous déclarez une méthode dans une classe dérivée
ayant le même nom qu’une méthode statique dans la classe ancêtre, la nouvelle
méthode remplace simplement celle héritée dans la classe dérivée.

Exemple de méthodes statiques


Dans le code suivant, le premier composant déclare deux méthodes statiques. Le
deuxième déclare deux méthodes statiques de même nom qui remplacent les
méthodes héritées du premier composant.
type
TFirstComponent = class(TComponent)
procedure Move;
procedure Flash;
end;
TSecondComponent = class(TFirstComponent)
procedure Move; { différent de la méthode héritée, malgré la même déclaration }
function Flash(HowOften: Integer): Integer; { c’est aussi différent }
end;

32-8 Guide du développeur


Répartition des méthodes

Méthodes virtuelles
Les méthodes virtuelles emploient un mécanisme de répartition plus compliqué
et plus souple que les méthodes statiques. Une méthode virtuelle peut être
redéfinie dans des classes descendantes, mais est toujours appelée dans la classe
ancêtre. L’adresse d’une méthode virtuelle n’est pas déterminée lors de la
compilation ; à la place, l’objet où la méthode est définie donne l’adresse lors de
l’exécution.
Pour qu’une méthode soit virtuelle, ajoutez la directive virtual après la
déclaration de la méthode. La directive virtual crée une entrée dans le tableau de
méthode virtuelle, de l’objet, ou VMT, qui contient les adresses de toutes les
méthodes virtuelles d’un type objet.
Lorsque vous dérivez une nouvelle classe d’une classe existante, la nouvelle
classe a son propre VMT, qui comprend toutes les entrées provenant du VMT de
l’ancêtre plus toutes les méthodes virtuelles supplémentaires déclarées dans la
nouvelle classe.

Surcharge des méthodes


Surcharger une méthode signifie l’étendre ou la redéfinir plutôt que la remplacer.
Une classe descendante peut surcharger toutes ses méthodes virtuelles héritées.
Pour surcharger une méthode dans une classe descendante, ajoutez la directive
override à la fin de la déclaration de méthode.
La surcharge d’une méthode provoque une erreur de compilation si
• La méthode n’existe pas dans la classe ancêtre.
• La méthode de l’ancêtre du même nom est statique.
• Les déclarations ne sont pas identiques (le numéro et le type des paramètres
arguments diffèrent).
Le code suivant montre la déclaration de deux composants simples. Le premier
déclare trois méthodes, chacune avec un type de répartition différent. L’autre,
dérivé du premier, remplace la méthode statique et surcharge les méthodes
virtuelles.
type
TFirstComponent = class(TCustomControl)
procedure Move; { méthode statique}
procedure Flash; virtual; { méthode virtuelle}
procedure Beep; dynamic; { méthode virtuelle dynamique}
end;
TSecondComponent = class(TFirstComponent)
procedure Move; { déclare une nouvelle méthode}
procedure Flash; override ; { surcharge la méthode héritée}
procedure Beep; override ; { surcharge la méthode héritée}
end;

Programmation orientée objet et écriture des composants 32-9


Membres abstraits d’une classe

Méthodes dynamiques
Les méthodes dynamiques sont des méthodes virtuelles avec un mécanisme de
répartition légèrement différent. Comme les méthodes dynamiques n’ont pas
d’entrées dans le tableau de méthode virtuelle de l’objet, elles peuvent réduire la
taille de la mémoire consommée par les objets. Cependant les méthodes de
répartition dynamiques sont quelque peu plus lentes que les méthodes de
répartition virtuelles normales. Si une méthode est fréquemment appelée, ou si
son exécution nécessite un temps court, vous devrez probablement la déclarer
virtuelle plutôt que dynamique.
Les objets doivent stocker les adresses de leurs méthodes dynamiques. Mais
plutôt que de recevoir les entrées dans le tableau de méthode virtuelle, les
méthodes dynamiques sont indiquées séparément. La liste des méthodes
dynamiques contient des entrées uniquement pour les méthodes introduites ou
surchargées par une classe particulière (le tableau de méthode virtuelle, à
l’inverse, comprend toutes les méthodes virtuelles de l’objet, à la fois héritées et
introduites). Les méthodes dynamiques héritées sont réparties en cherchant
chaque liste de méthode dynamique de l’ancêtre, en allant en arrière dans
l’arborescence de l’héritage.
Pour rendre une méthode dynamique, ajoutez la directive dynamic après la
déclaration de méthode.

Membres abstraits d’une classe


Lorsqu’une méthode est déclarée abstract dans une classe ancêtre, vous devez la
surfacer (c’est-à-dire la redéclarer et l’implémenter) dans tout composant
descendant avant d’utiliser le nouveau composant dans les applications. Delphi
ne peut créer d’instance d’une classe contenant des membres abstraits. Pour plus
d’informations sur le surfaçage des constituants hérités des classes, voir
chapitre 33, “Création de propriétés”, et chapitre 35, “Création de méthodes”.

Classes et pointeurs
Chaque classe (et par conséquent chaque composant) est en fait un pointeur. Le
compilateur déréférence automatiquement les pointeurs de classe à votre place,
aussi n’avez-vous généralement pas besoin de vous poser ces questions. Le statut
des classes en tant que pointeurs devient important lorsque vous passez une
classe comme paramètre. En général, vous transmettrez les classes par valeur
plutôt que par référence. Car les classes sont déjà des pointeurs, c’est-à-dire des
références ; transmettre une classe par référence serait transmettre une référence à
une référence.

32-10 Guide du développeur


Chapitre

33
Création de propriétés
Chapter 33

Les propriétés sont la partie la plus visible des composants. Le développeur


d’applications a la possibilité de les voir et de les manipuler au moment de la
conception et dispose d’un retour immédiat au fur et à mesure que réagissent les
composants dans le concepteur de fiches. Les propriétés conçues correctement
facilitent l’utilisation de vos composants par d’autres personnes et leur
maintenance par vous-même.
Pour utiliser de façon optimale les propriétés de vos composants, vous devez
connaître les points suivants :
• Pourquoi créer des propriétés ?
• Types de propriétés
• Publication des propriétés héritées
• Définition des propriétés
• Création de propriétés tableau
• Stockage et chargement des propriétés

Pourquoi créer des propriétés ?


Du point de vue du développeur d’applications, les propriétés ressemblent à des
variables. Les développeurs peuvent définir ou lire les valeurs des propriétés
comme s’il s’agissait de champs. La seule opération interdite avec une propriété
et autorisée avec une variable consiste à la transmettre comme paramètre var.
Les propriétés ont une puissance bien supérieure à celle de simples champs car
• Les développeurs d’applications peuvent définir des propriétés au moment de
la conception. Contrairement aux méthodes, qui ne sont accessibles qu’à
l’exécution, les propriétés permettent au développeur de personnaliser les
composants avant l’exécution de l’application. Les propriétés apparaissent

Création de propriétés 33-1


Types de propriétés

dans l’inspecteur d’objets, ce qui simplifie le travail du programmeur ; au lieu


de traiter plusieurs paramètres pour construire un objet, il laisse Delphi lire
des valeurs dans l’inspecteur d’objets. L’inspecteur d’objets valide les
affectations des valeurs aux propriétés dès qu’elles sont effectuées.
• Les propriétés peuvent masquer les détails de l’implémentation. Par exemple,
des données stockées de façon interne sous une forme cryptée peuvent
apparaître non cryptées en tant que la valeur d’une propriété ; bien que la
valeur puisse être un simple nombre, le composant peut rechercher cette
valeur dans une base de données ou effectuer des calculs complexes afin de la
récupérer. Les propriétés permettent d’associer des opérations complexes à
une simple affectation ; ce qui apparaît comme l’affectation d’un champ
correspond en fait à un appel de méthode et cette dernière peut accomplir à
peu près n’importe quelle tâche.
• Les propriétés peuvent être virtuelles. Ce qui paraît être une seule propriété
pour le développeur d’applications peut être implémenté de manière
différente dans des composants différents.
Un exemple simple est la propriété Top que tous les contrôles possèdent.
L’attribution d’une nouvelle valeur à Top n’a pas pour seul effet de modifier une
valeur mémorisée ; elle provoque aussi le déplacement et le réaffichage du
contrôle. Les effets de la définition d’une propriété ne se limitent pas
nécessairement à un composant unique ; par exemple, donner la valeur True à la
propriété Down d’un turbobouton a pour effet d’attribuer la valeur False à tous
les autres turboboutons du même groupe.

Types de propriétés
Une propriété peut avoir un type quelconque. Les divers types sont affichés de
manière différente dans l’inspecteur d’objets, ce qui valide l’affectation des
propriétés effectuées au moment de la conception.

Tableau 33.1 Affichage des propriétés dans l’inspecteur d’objets


Type de
propriété Traitement de l’inspecteur d’objets
Simple Les propriétés de type numérique, caractère et chaîne apparaissent dans
l’inspecteur d’objets comme des nombres, caractères et chaînes. Le
développeur d’applications peut modifier directement la valeur de ces
propriétés.
Enuméré Les propriétés de type énuméré (y compris le type Boolean) apparaissent
comme des chaînes éditables. Le développeur peut également passer en
revue toutes les valeurs possibles en double-cliquant sur la colonne
contenant la valeur et il existe une liste déroulante montrant toutes les
valeurs possibles.
Ensemble Les propriétés de type ensemble apparaissent dans l’inspecteur d’objets
comme des ensembles. En double-cliquant sur la propriété, le développeur
peut développer l’ensemble et traiter chacun des éléments comme une
valeur booléenne (true si cet élément appartient à l’ensemble).

33-2 Guide du développeur


Publication des propriétés héritées

Tableau 33.1 Affichage des propriétés dans l’inspecteur d’objets (suite)


Type de
propriété Traitement de l’inspecteur d’objets
Objet Les propriétés qui sont elles-mêmes des classes ont souvent leur propre
éditeur de propriétés, qui est spécifié dans la procédure de recensement du
composant. Si la classe d’une propriété a ses propres propriétés publiées
(published), l’inspecteur d’objets permet au développeur d’étendre la liste
(en double-cliquant) afin d’inclure ces propriétés et de les modifier
individuellement. Les propriétés doivent descendre de TPersistent.
Tableau Les propriétés tableau doivent disposer d’un éditeur de propriétés
spécifique. L’inspecteur d’objets ne dispose d’aucune fonction intégrée
permettant de modifier les propriétés de ce type. Vous pouvez spécifier un
éditeur de propriétés lorsque vous recensez vos composants.

Publication des propriétés héritées


Tous les composants héritent des propriétés de leurs classes ancêtre. Lorsque
vous dérivez un composant à partir d’un composant existant, le nouveau
composant hérite de toutes les propriétés de l’ancêtre immédiat. Si vous effectuez
la dérivation à partir d’une des classes abstraites, aucune des propriétés héritées
n’est published, la plupart sont protected ou public.
Pour rendre disponible dans l’inspecteur d’objets une propriété protected ou
private au moment de la conception, vous devez redéclarer published la
propriété. Redéclarer une propriété signifie ajouter la déclaration d’une propriété
héritée à la déclaration de la classe descendante.
Si vous dérivez un composant de TWinControl, par exemple, il hérite de la
propriété protégée Ctl3D. En redéclarant Ctl3D dans votre nouveau composant,
vous pouvez changer le niveau de protection en public ou publié.
Le code suivant montre une redéclaration de Ctl3D en publié, le rendant
disponible lors de la conception.
type
TSampleComponent = class(TWinControl)
published
property Ctl3D;
end;
Lorsque vous redéclarez une propriété, vous spécifiez uniquement le nom de la
propriété, non le type ni les autres informations décrites ci-dessous dans
“Définition des propriétés”. Vous pouvez aussi déclarer de nouvelles valeurs par
défaut et spécifier si la propriété est ou non stockée.
Les redéclarations peuvent augmenter la visibilité d’une propriété, mais pas la
réduire. Vous pouvez ainsi rendre une propriété protégée publique, mais vous
ne pouvez pas masquer une propriété publique en la redéclarant protégée.

Création de propriétés 33-3


Définition des propriétés

Définition des propriétés


Cette section montre comment déclarer de nouvelles propriétés et explique certaines
conventions respectées par les composants standard. Ces rubriques comprennent :
• Déclaration des propriétés
• Stockage interne des données
• Accès direct
• Méthodes d’accès
• Valeurs par défaut d’une propriété

Déclaration des propriétés


Une propriété est déclarée dans la déclaration de sa classe composant. Pour
déclarer une propriété, vous devez spécifier les trois éléments suivants :
• Le nom de la propriété.
• Le type de la propriété.
• Les méthodes utilisées pour lire et écrire la valeur de la propriété. Si aucune
méthode d’écriture n’est déclarée, la propriété est accessible uniquement en
lecture.
Les propriétés déclarées dans une section published de la déclaration de classe
du composant sont modifiables dans l’inspecteur d’objets lors de la conception.
La valeur d’une propriété published est enregistrée avec le composant dans le
fichier fiche. Les propriétés déclarées dans une section public sont accessibles à
l’exécution et peuvent être lues ou définies par le code du programme.
Voici une déclaration typique pour une propriété appelée Count.
type
TYourComponent = class(TComponent)
private
FCount: Integer; { utilisé pour le stockage interne }
procedure SetCount (Value: Integer); { méthode d’écriture}
public
property Count: Integer read FCount write SetCount;
end;

Stockage interne des données


Il n’existe aucune restriction quant au stockage des données d’une propriété.
Toutefois, les composants Delphi respectent généralement les conventions
suivantes :
• Les données des propriétés sont stockées dans des champs.
• Les champs utilisés pour stocker les données d’une propriété sont déclarés
private et ne peuvent être accédées qu’à partir du composant lui-même. Les
composants dérivés doivent utiliser la propriété héritée ; ils ne nécessitent pas
un accès direct au stockage interne des données de la propriété.

33-4 Guide du développeur


Définition des propriétés

• Les identificateurs de ces champs sont composés de la lettre F suivie du nom


de la propriété. Par exemple, la donnée brute de la propriété Width définie
pour TControl est stockée dans un champ appelé FWidth.
Le principe qui sous-tend ces conventions est le suivant : seules les méthodes
d’implémentation d’une propriété doivent pouvoir accéder aux données associées
à cette propriété. Si une méthode ou une autre propriété a besoin de changer ces
données, elle doit le faire via la propriété et non directement par un accès aux
données stockées. Cela garantit que l’implémentation d’une propriété héritée
puisse être modifiée sans invalider les composants dérivés.

Accès direct
L’accès direct est le moyen le plus simple d’accéder aux données d’une propriété.
Autrement dit, les parties read et write de la déclaration d’une propriété
spécifient que l’affectation ou la lecture de la valeur de la propriété s’effectue
directement dans le champ de stockage interne sans appel à une méthode
d’accès. L’accès direct est utile lorsque vous voulez rendre une propriété
accessible dans l’inspecteur d’objets, mais que vous ne voulez pas que le
changement de sa valeur déclenche un processus immédiatement.
En général, vous définirez un accès direct pour la partie read d’une déclaration
de propriété et utiliserez une méthode d’accès pour la partie write. Cela permet
de mettre à jour l’état du composant lorsque la valeur de la propriété change.
La déclaration de type composant suivante montre une propriété qui utilise
l’accès direct pour les parties read et write.
type
TSampleComponent = class(TComponent)
private { le stockage interne est privé}
FMyProperty: Boolean; { déclare la donnée membre pour contenir la valeur }
published { rend la propriété disponible à la conception }
property MyProperty: Boolean read FMyProperty write FMyProperty;
end;

Méthodes d’accès
Vous pouvez spécifier une méthode d’accès plutôt qu’un champ dans les parties
read et write d’une déclaration de propriété. Les méthodes d’accès doivent être
protected, et sont habituellement déclarées comme virtual ; cela autorise les
composants descendants à surcharger l’implémentation de la propriété.
Evitez de rendre publiques les méthodes d’accès. Les conserver protected vous
prémunit contre toute modification accidentelle d’une propriété par un
développeur d’applications qui appellerait ces méthodes.

Création de propriétés 33-5


Définition des propriétés

Voici une classe qui déclare trois propriétés en utilisant le spécificateur d’index,
qui autorise aux trois propriétés d’avoir les mêmes méthodes d’accès en lecture
et en écriture :
type
TSampleCalendar = class(TCustomGrid)
public
property Day: Integer index 3 read GetDateElement write SetDateElement;
property Month: Integer index 2 read GetDateElement write SetDateElement;
property Year: Integer index 1 read GetDateElement write SetDateElement;
private
function GetDateElement(Index: Integer): Integer; { remarquez le paramètre Index }
procedure SetDateElement(Index: Integer; Value: Integer);
ƒ
Comme chaque élément de la date (day, month et year) est un int et comme la
définition de chacun requiert le codage de la date lorsqu’elle est définie, le code
évite la duplication en partageant les méthodes de lecture et d’écriture pour les
trois propriétés. Vous n’avez besoin que d’une seule méthode pour lire un
élément date et une autre pour écrire l’élément date.
Voici la méthode read qui obtient l’élément date :
function TSampleCalendar.GetDateElement(Index: Integer): Integer;
var
AYear, AMonth, ADay: Word;
begin
DecodeDate(FDate, AYear, AMonth, ADay); { décompose la date codée en éléments }
case Index of
1: Result := AYear;
2: Result := AMonth;
3: Result := ADay;
else Result := -1;
end;
end;
Voici la méthode write qui définit l’élément date approprié :
procedure TSampleCalendar.SetDateElement(Index: Integer; Value: Integer);
var
AYear, AMonth, ADay: Word;
begin
if Value > 0 then { tous les éléments doivent être positifs}
begin
DecodeDate(FDate, AYear, AMonth, ADay); { prend les éléments date actuels }
case Index of { définit le nouvel élément selon Index }
1: AYear := Value;
2: AMonth := Value;
3: ADay := Value;
else Exit;
end;
FDate := EncodeDate(AYear, AMonth, ADay); { code la date modifiée}
Refresh; { actualise le calendrier visible }
end;
end;

33-6 Guide du développeur


Définition des propriétés

Méthode read
La méthode read d’une propriété est une fonction qui n’accepte aucun paramètre
(sauf pour ce qui est mentionné ci-après) et renvoie une valeur du même type
que la propriété. Par convention, le nom de la fonction est Get suivi du nom de
la propriété. Par exemple, la méthode read pour une propriété intitulée Count
serait GetCount. La méthode read manipule les données internes afin de générer
une valeur de la propriété respectant le type demandé.
Les seules exceptions à la règle “aucun paramètre” sont les propriétés tableau et
les propriétés qui utilisent un spécificateur d’index (voir “Création de propriétés
tableau” à la page 33-9), pour lesquelles cet index est transmis comme paramètre.
Utilisez des spécificateurs d’index pour créer une méthode read unique partagée
par plusieurs propriétés. Pour plus d’informations sur les spécificateurs d’index,
consultez le guide du langage Pascal Objet.
Si vous ne déclarez aucune méthode read, la propriété fonctionne uniquement en
écriture. Les propriétés fonctionnant en écriture uniquement sont très rares.

Méthode write
La méthode write d’une propriété est une procédure acceptant un seul paramètre
(sauf pour ce qui est mentionné ci-après) du même type que la propriété. Le
paramètre peut être transmis par référence ou par valeur et peut porter le nom de
votre choix. Par convention, le nom de la méthode write est Set suivi du nom de
la propriété. Par exemple, la méthode write d’une propriété intitulée Count serait
SetCount. La valeur transmise en paramètre devient la nouvelle valeur de la
propriété ; la méthode write doit accomplir les manipulations nécessaires pour
placer les données concernées à l’emplacement de stockage interne de la propriété.
Les seules exceptions à la règle “paramètre unique” sont les propriétés tableau et
les propriétés qui utilisent un spécificateur d’index, pour lesquelles cet index est
transmis comme second paramètre. Utilisez des spécificateurs d’index pour créer
une méthode read unique partagée par plusieurs propriétés. Pour plus
d’informations sur les spécificateurs d’index, consultez le guide du langage Pascal
Objet.
Si vous ne déclarez aucune méthode write, la propriété fonctionne uniquement
en lecture. Pour qu’une propriété publiée puisse être utilisée au moment de la
conception, elle doit être accessible en lecture/écriture.
Les méthodes write testent normalement si une nouvelle valeur diffère de la
valeur actuelle avant de modifier la propriété. Par exemple, voici une méthode
write simple d’une propriété de type entier appelée Count stockant sa valeur
courante dans un champ appelé FCount.
procedure TMyComponent.SetCount(Value: Integer);
begin
if Value <> FCount then
begin
FCount := Value;
Update;
end;
end;

Création de propriétés 33-7


Définition des propriétés

Valeurs par défaut d’une propriété


Lorsque vous déclarez une propriété, vous pouvez déclarer une valeur par défaut.
Delphi utilise cette valeur par défaut pour déterminer si une propriété doit être
stockée dans un fichier fiche. Si vous ne donnez pas de valeur par défaut à une
propriété, Delphi stocke toujours cette propriété.
Pour spécifier une valeur par défaut pour une propriété, ajoutez la directive
default à la déclaration (ou à la redéclaration) de la propriété, suivie par la
valeur par défaut. Par exemple,
property Cool Boolean read GetCool write SetCool default True;
Remarque Déclarer une valeur par défaut pour une propriété n’a pas pour effet de définir
cette propriété par cette valeur. La méthode constructeur du composant doit
initialiser la valeur des propriétés lorsque c’est nécessaire. Toutefois, comme les
objets initialisent toujours leurs champs à 0, il n’est pas nécessaire que le
constructeur initialise les propriétés entières à 0, les propriétés chaînes à null ni
les propriétés booléennes à False.

Spécification d’aucune valeur par défaut


Lorsque vous redéclarez une propriété, vous pouvez indiquer que la propriété ne
possède pas de valeur par défaut, même si une telle valeur est définie pour la
propriété reçue en héritage.
Pour indiquer qu’une propriété n’a pas de valeur par défaut, ajoutez la directive
nodefault à la déclaration de la propriété. Par exemple,
property FavoriteFlavor string nodefault ;
Lorsque vous déclarez une propriété pour la première fois, vous n’êtes pas
obligé de spécifier nodefault car, dans ce cas, l’absence de valeur par défaut a la
même signification.
Voici la déclaration d’un composant qui comprend une propriété booléenne
unique appelée IsTrue dont la valeur par défaut est True. La déclaration ci-
dessous (dans la section implémentation de l’unité) représente le constructeur
qui initialise la propriété.
type
TSampleComponent = class(TComponent)
private
FIsTrue: Boolean;
public
constructor Create(AOwner: TComponent); override ;
published
property IsTrue: Boolean read FIsTrue write FIsTrue default True;
end;
ƒ
constructor TSampleComponent.Create(AOwner: TComponent);
begin
inherited Create(AOwner); { appelle le constructeur hérité }
FIsTrue := True; { définit la valeur par défaut}
end;

33-8 Guide du développeur


Création de propriétés tableau

Création de propriétés tableau


Certaines propriétés se prêtent à l’indexation. Par exemple, la propriété Lines de
TMemo est la liste indexée des chaînes qui constituent le texte du mémo et vous
pouvez la traiter comme un tableau de chaînes. Lines fournit un accès à un
élément particulier (une chaîne) dans un ensemble plus large de données (le
texte du mémo).
Les propriétés tableau sont déclarées comme les autres propriétés. Les seules
différences sont les suivantes :
• La déclaration de la propriété doit comprendre un ou plusieurs index ayant
chacun un type défini. Les index peuvent avoir n’importe quel type.
• Les parties read et write de la déclaration de la propriété, lorsqu’elles sont
spécifiées, doivent être des méthodes. Il ne peut s’agir de champs.
Les méthodes read et write d’une propriété tableau acceptent des paramètres
supplémentaires correspondant aux index. Les paramètres doivent respecter
l’ordre et le type des index spécifiés dans la déclaration.
Bien qu’ils se ressemblent, il existe quelques différences importantes entre les
tableaux et les propriétés tableau. Contrairement aux indices d’un tableau,
l’index d’une propriété tableau n’est pas obligatoirement de type entier. Par
exemple, vous pouvez indexer une propriété en utilisant une chaîne. En outre,
vous ne pouvez référencer qu’un seul élément d’une propriété et non une plage
d’éléments.
L’exemple suivant est la déclaration d’une propriété renvoyant une chaîne en
fonction de la valeur d’un index de type entier.
type
TDemoComponent = class(TComponent)
private
function GetNumberName(Index: Integer): string;
public
property NumberName[Index: Integer]: string read GetNumberName;
end;
ƒ
function TDemoComponent.GetNumberName(Index: Integer): string;
begin
Result := 'Unknown';
case Index of
-MaxInt..-1: Result := 'Negative';
0: Result := 'Zero';
1..100: Result := 'Small';
101..MaxInt: Result := 'Large';
end;
end;

Création de propriétés 33-9


Stockage et chargement des propriétés

Stockage et chargement des propriétés


Delphi stocke les fiches et leurs composants dans des fichiers fiche (.DFM). Un
fichier fiche est une représentation binaire des propriétés d’une fiche et de ses
composants. Lorsque les développeurs Delphi ajoutent à leurs fiches les
composants que vous avez écrits, vos composants doivent être capables
d’enregistrer leurs propriétés dans le fichier fiche lors de sa sauvegarde. De
même, lorsqu’ils sont chargés dans Delphi ou exécutés comme éléments d’une
application, vos composants doivent être capables de se restituer eux-mêmes à
partir du fichier fiche.
La plupart du temps, vous n’aurez rien à faire pour que vos composants
fonctionnent avec un fichier fiche car la fonction de stockage et de chargement
d’une représentation fait partie du comportement reçu en héritage par tous les
composants. Toutefois, dans certaines circonstances, vous pouvez souhaiter
modifier le stockage d’un composant ou son initialisation au chargement. C’est
pourquoi il est conseillé de comprendre les mécanismes sous-jacents.
Les aspects du stockage de propriétés qu’il est nécessaire d’expliquer sont les
suivants :
• Utilisation du mécanisme de stockage et de chargement
• Spécification des valeurs par défaut
• Détermination du stockage
• Initialisation après chargement
• Stockage et chargement des propriétés non publiées

Utilisation du mécanisme de stockage et de chargement


La description d’une fiche est la liste des propriétés de la fiche accompagnée
d’une liste semblable pour chacun de ses composants. Chaque composant, y
compris la fiche elle-même, est responsable du stockage et du chargement de sa
propre description.
Lorsqu’il se stocke lui-même, un composant écrit implicitement les valeurs de
toutes ses propriétés publiques ou publiées si celles-ci sont différentes de leurs
valeurs par défaut, en respectant l’ordre dans lequel ont été déclarées ces
valeurs. Au chargement, le composant commence par se construire lui-même,
toutes les propriétés récupérant leurs valeurs par défaut, puis il lit les valeurs
stockées des propriétés dont les valeurs ne correspondent pas aux valeurs par
défaut.
Ce mécanisme implicite répond à la plupart des besoins des composants et ne
nécessite aucune intervention particulière de la part de l’auteur du composant.
Néanmoins, il existe plusieurs moyens de personnaliser le processus de stockage
et de chargement pour répondre aux besoins particuliers d’un composant.

33-10 Guide du développeur


Stockage et chargement des propriétés

Spécification des valeurs par défaut


Les composants Delphi ne stockent la valeur des propriétés que si elles diffèrent
des valeurs par défaut. Sauf indication contraire, Delphi suppose qu’une
propriété n’a pas de valeur par défaut, ce qui a pour conséquence que le
composant stocke toujours la propriété, quelle que soit sa valeur.
Pour spécifier une valeur par défaut pour une propriété, ajoutez la directive
default et la nouvelle valeur par défaut à la fin de la déclaration de la propriété.
Vous pouvez également spécifier une valeur par défaut en redéclarant une
propriété. En fait, l’attribution d’une autre valeur par défaut est l’une des raisons
qui peut vous amener à redéclarer une propriété.
Remarque La spécification d’une valeur par défaut n’a pas pour effet d’attribuer cette
valeur à la propriété lorsque l’objet est créé. Le constructeur du composant doit
s’en charger. Une propriété dont la valeur n’est pas définie par le constructeur
du composant, a la valeur zéro, ou la valeur affichée par la propriété quand son
stockage en mémoire est 0. Par défaut, les nombres valent donc 0, les booléens
False, les pointeurs nil, etc. En cas de doute, affectez une valeur dans la méthode
du constructeur.
Le code suivant montre la déclaration d’un composant qui attribue une valeur
par défaut à la propriété Align, ainsi que l’implémentation du constructeur du
composant qui affecte cette valeur. Dans notre exemple, le nouveau composant
est un cas particulier du composant volet standard utilisé en tant que barre
d’état dans une fenêtre. Il doit donc implicitement s’aligner sur la bordure
inférieure de son propriétaire.
type
TStatusBar = class(TPanel)
public
constructor Create(AOwner: TComponent); override ; { surcharge pour définir la
// nouvelle valeur par défaut }
published
property Align default alBottom; { redéclare avec la nouvelle valeur par défaut }
end;
ƒ
constructor TStatusBar.Create(AOwner: TComponent);
begin
inherited Create(AOwner); { effectue une initialisation héritée }
Align := alBottom; { affecte une nouvelle valeur par défaut à Align }
end;

Détermination du stockage
Vous pouvez choisir si Delphi stocke ou non chacune des propriétés de vos
composants. Par défaut, sont stockées toutes les propriétés de la partie published
de la déclaration de classe. Vous pouvez choisir de ne pas stocker une propriété
ou de désigner une fonction qui décidera, au moment de l’exécution, du
stockage de la propriété.

Création de propriétés 33-11


Stockage et chargement des propriétés

Pour contrôler le stockage par Delphi d’une propriété, ajoutez la directive stored
à la déclaration de propriété, suivie par true, false ou le nom d’une méthode
booléenne.
Le code suivant montre un composant avec la déclaration de trois nouvelles
propriétés. La première est toujours stockée, la deuxième ne l’est jamais et la
troisième est stockée selon la valeur d’une méthode booléenne :
type
TSampleComponent = class(TComponent)
protected
function StoreIt: Boolean;
public { non stockée normalement}
property Important: Integer stored True; { toujours stockée}
published { toujours stockée normalement}
property Unimportant: Integer stored False; { jamais stockée}
property Sometimes: Integer stored StoreIt; { dépend de la valeur de la fonction}
end;

Initialisation après chargement


Après qu’un composant a lu les valeurs de toutes ses propriétés dans sa
description stockée, il appelle une méthode virtuelle appelée Loaded, qui effectue
toutes les initialisations nécessaires. L’appel de Loaded s’exécute avant que ne
s’affichent la fiche et ses contrôles, ainsi vous n’avez pas à vous soucier du
scintillement de l’écran provoqué par ces initialisations.
Pour initialiser un composant après le chargement des valeurs des propriétés,
vous devez surcharger la méthode Loaded.
Remarque La première opération à accomplir dans une méthode Loaded consiste à appeler
la méthode Loaded reçue en héritage. Ceci afin d’être sûr que toutes les
propriétés reçues en héritage sont correctement initialisées avant d’effectuer
l’initialisation de votre propre composant.
Le code suivant provient du composant TDatabase. Après chargement, la base de
données essaie de rétablir toutes les connexions ouvertes au moment du
stockage, et spécifie comment gérer toutes les exceptions qui se produisent
pendant la connexion.
procedure TDatabase.Loaded;
begin
inherited Loaded; { appelle d’abord la méthode héritée}
try
if FStreamedConnected then Open { rétablit les connexions }
else CheckSessionName(False);
except
if csDesigning in ComponentState then { lors de la conception... }
Application.HandleException(Self) { permet à Delphi de gérer l’exception }
else raise; { sinon, redéclenche }
end;
end;

33-12 Guide du développeur


Stockage et chargement des propriétés

Stockage et chargement des propriétés non publiées


Par défaut, seules les propriétés publiées sont chargées et enregistrées avec un
composant. Cependant, il est possible de charger et d’enregistrer des propriétés
non publiées. Ceci permet d’obtenir des propriétés persistantes n’apparaissant pas
dans l’inspecteur d’objets. Cela permet aussi le stockage et le chargement des
valeurs de propriétés par les composants, valeurs de propriétés que Delphi ne sait
pas comment lire ni écrire car elles sont trop complexes. Par exemple, l’objet
TStrings ne peut pas compter sur le comportement automatique de Delphi pour
stocker et charger les chaînes qu’il représente et doit utiliser le mécanisme suivant.
Vous pouvez enregistrer des propriétés non publiées en ajoutant du code
indiquant à Delphi comment charger et enregistrer la valeur de propriété.
Pour écrire votre propre code afin de charger et d’enregistrer des propriétés,
utilisez les étapes suivantes :
1 Création de méthodes pour le stockage et le chargement de valeurs de propriétés.
2 Redéfinition de la méthode DefineProperties , en transmettant ces méthodes à
un objet filer.

Création de méthodes pour le stockage et le chargement de valeurs de


propriétés
Pour stocker et charger des propriétés non publiées, vous devez d’abord créer
une méthode afin de stocker la valeur de propriété et une autre méthode pour la
charger. Deux possibilités s’offrent à vous :
• Créer une méthode de type TWriterProc afin de stocker la valeur de propriété
et une méthode de type TReaderProc pour la charger. Cette approche vous
permet de profiter des capacités intégrées de Delphi concernant
l’enregistrement et le chargement de types simples. Si la valeur de votre
propriété est construite à partir de types que Delphi sait enregistrer et charger,
utilisez cette approche.
• Créer deux méthodes de type TStreamProc, une pour stocker et une pour charger
la valeur de propriété. TStreamProc accepte un flux comme argument et vous
pouvez utiliser les méthodes du flux afin d’écrire et lire les valeurs de propriétés.
Prenons pour exemple une propriété représentant un composant créé à
l’exécution. Delphi sait comment écrire cette valeur, mais ne le fait pas
automatiquement car le composant n’est pas créé dans le concepteur de fiches.
Puisque le système de flux peut dès à présent charger et enregistrer des
composants, vous pouvez utiliser la première approche. Les méthodes suivantes
chargent et stockent le composant créé dynamiquement qui représente la valeur
d’une propriété appelée MyCompProperty :
procedure TSampleComponent.LoadCompProperty(Reader: TReader);
begin
if Reader.ReadBoolean then
MyCompProperty := Reader.ReadComponent(nil);
end;

Création de propriétés 33-13


Stockage et chargement des propriétés

procedure TSampleComponent.StoreCompProperty(Writer: TWriter);


begin
Writer.WriteBoolean(MyCompProperty <> nil);
if MyCompProperty <> nil then
Writer.WriteComponent(MyCompProperty);
end;

Redéfinition de la méthode DefineProperties


Après avoir créé des méthodes de stockage et de chargement de la valeur de
propriété, vous pouvez redéfinir la méthode DefineProperties du composant.
Delphi appelle cette méthode lors du chargement ou du stockage du composant.
Dans la méthode DefineProperties, vous devez appeler la méthode DefineProperty
ou DefineBinaryProperty du filer en cours, en lui transmettant la méthode à
utiliser pour le chargement ou l’enregistrement de la valeur de propriété. Si vos
méthodes de chargement et de stockage sont des types TWriterProc et
TReaderProc, vous appelez alors la méthode DefineProperty du filer. Si vous avez
créé des méthodes de type TStreamProc, appelez plutôt DefineBinaryProperty.
Peu importe la méthode utilisée pour définir la propriété, vous lui transmettez
les méthodes enregistrant et chargeant votre valeur de propriété ainsi qu’une
valeur booléenne indiquant si la valeur de propriété doit être écrite ou non. S’il
peut s’agir d’une valeur héritée ou si elle a une valeur par défaut, il n’est pas
nécessaire de l’écrire.
Soit par exemple la méthode LoadCompProperty du type TReaderProc et la
méthode StoreCompProperty du type TWriterProc, vous redéfiniriez DefineProperties
de la manière suivante :
procedure TSampleComponent.DefineProperties(Filer: TFiler);
function DoWrite: Boolean;
begin
if Filer.Ancestor <> nil then { recherche l’ancêtre pour une valeur héritée }
begin
if TSampleComponent(Filer.Ancestor).MyCompProperty = nil then
Result := MyCompProperty <> nil
else if MyCompProperty = nil or
TSampleComponent(Filer.Ancestor).MyCompProperty.Name <> MyCompProperty.Name then
Result := True
else Result := False;
end
else { aucune valeur héritée-- cherche la valeur par défaut (nil) }
Result := MyCompProperty <> nil;
end;
begin
inherited ; { autorise la définition des propriétés par les classes de base}
Filer.DefineProperty('MyCompProperty', LoadCompProperty, StoreCompProperty, DoWrite);
end;

33-14 Guide du développeur


Chapitre

34
Création d’événements
Chapter 34

Un événement est un lien entre une occurrence du système (une action de


l’utilisateur ou un changement de focalisation, par exemple) et le fragment de code
qui assure effectivement la réponse. Le code de réponse est le gestionnaire de
l’événement, dont l’écriture est presque toujours du ressort du développeur de
l’application. Grâce aux événements, les développeurs peuvent personnaliser le
comportement des composants sans avoir besoin de modifier les classes elles-
mêmes. Cela s’appelle la délégation.
Les événements associés aux actions utilisateur les plus usuelles (par exemple, les
actions de la souris) sont intégrés dans les composants standard, mais vous pouvez
aussi définir de nouveaux événements. Pour être capable de créer des événements
dans un composant, vous devez avoir compris ce qui suit :
• Qu’est-ce qu’un événement ?
• Implémentation des événements standard
• Définition de vos propres événements
Les événements étant implémentés en tant que propriétés, il vaut mieux avoir
bien compris le chapitre 33, “Création de propriétés”, avant de créer ou de
modifier les événements d’un composant.

Qu’est-ce qu’un événement ?


Un événement est un mécanisme qui établit un lien entre une occurrence et une
partie de code. Plus précisément, un événement est un pointeur de méthodequi
pointe sur une méthode dans une instance de classe spécifique.
Du point de vue du développeur d’applications, l’événement est simplement un
nom relié à une occurrence du système, comme OnClick, auquel du code peut
être attaché. Par exemple, un bouton-poussoir appelé Button1 dispose d’une
méthode OnClick. Par défaut, Delphi génère l’événement appelé Button1Click dans

Création d’événements 34-1


Qu’est-ce qu’un événement ?

la fiche contenant le bouton et l’associe à l’événement OnClick. Lorsqu’un


événement clic de souris se produit sur le bouton, ce dernier appelle la méthode
associée à OnClick qui est pour notre exemple Button1Click.
Pour écrire un événement, vous devez comprendre ceci :

L’utilisateur clique Button1.OnClick pointe Form1.Button1Click


sur Button1 sur Form1.Button1Click s’exécute

Occurrence Evénement Gestionnaire d’événement

• Les événements sont des pointeurs de méthodes.


• Les événements sont des propriétés.
• Les types d’événements sont des types de pointeurs de méthodes
• Les types gestionnaire d’événement sont des procédures
• Les gestionnaires d’événements sont facultatifs.

Les événements sont des pointeurs de méthodes


Delphi utilise les pointeurs de méthodes pour implémenter les événements. Un
pointeur de méthode est un type particulier de pointeur qui pointe sur une
méthode spécifique située dans une instance d’objet. En tant qu’auteur de
composant, vous pouvez voir les pointeurs de méthodes comme des marques de
réservation. Après la détection d’un événement par votre code, vous appelez la
méthode (si elle existe) définie par l’utilisateur pour gérer cet événement.
Les pointeurs de méthodes fonctionnent comme les autres types procéduraux,
mais ils maintiennent un pointeur caché sur un objet. Quand le développeur
d’applications associe un gestionnaire à un événement du composant,
l’association ne s’effectue pas seulement avec une méthode ayant un nom
particulier, mais avec une méthode d’une instance d’objet particulière. Cet objet
est généralement la fiche contenant le composant, mais ce n’est pas toujours le
cas.
Tous les contrôles, par exemple, héritent d’une méthode dynamique appelée Click
pour la gestion des événements de clic avec la souris :
procedure Click; dynamic;
L’implémentation de Click appelle le gestionnaire utilisateur des événements liés
aux clics de la souris, s’il existe. Si l’utilisateur a affecté un gestionnaire à
l’événement OnClick d’un contrôle, le fait de cliquer sur ce dernier génère
l’appel de la méthode. Si aucun gestionnaire n’est affecté, rien ne se produit.

Les événements sont des propriétés


Ces composants utilisent les propriétés pour implémenter les événements. Mais,
contrairement à la plupart des propriétés, les propriétés événement ne font pas
appel à des méthodes pour implémenter leurs parties read et write. Elles utilisent
plutôt un champ de classe de même type que la propriété.

34-2 Guide du développeur


Qu’est-ce qu’un événement ?

Par convention, le nom de la donnée membre est le même que celui de la


propriété précédé de la lettre F. Par exemple, le pointeur de la méthode OnClick
est stocké dans une donnée membre intitulée FOnClick de type TNotifyEvent ; la
déclaration de la propriété de l’événement OnClick ressemble à ceci :
type
TControl = class(TComponent)
private
FOnClick: TNotifyEvent; { déclare une donnée membre pour contenir
// le pointeur de méthode }
ƒ
protected
property OnClick: TNotifyEvent read FOnClick write FOnClick;
end;
Pour en savoir davantage sur TNotifyEvent et sur les autres types d’événements,
voir la prochaine section, “Les types d’événements sont des types de pointeurs
de méthodes”.
Comme pour toute autre propriété, vous pouvez définir ou modifier la valeur d’un
événement au moment de l’exécution. L’avantage principal des événements
implémentés sous la forme de propriétés est que l’utilisateur de composant peut lui
associer un gestionnaire au moment de la conception à l’aide de l’inspecteur
d’objets.

Les types d’événements sont des types de pointeurs de méthodes


Comme un événement est un pointeur sur un gestionnaire d’événement, le type
d’une propriété événement correspond nécessairement à un pointeur de
méthode. De même, tout code utilisé comme gestionnaire d’événement doit être
de type méthode d’objet.
Toutes les méthodes gestionnaire d’événement sont des procédures. Pour être
compatible avec un événement d’un type particulier, une méthode gestionnaire
d’événement doit avoir le même nombre de paramètres, les paramètres étant de
même type et transmis dans le même ordre.
Delphi définit des types de méthode pour tous les événements standard. Lorsque
vous créez vos propres événements, vous pouvez utiliser un type existant s’il est
approprié ou définir votre propre type.

Les types gestionnaire d’événement sont des procédures


Bien que le compilateur vous permet de déclarer des types pointeur de méthode
qui sont des fonctions, vous ne devrez jamais le faire pour la gestion
d’événements. Comme une fonction vide renvoie un résultat non défini, un
gestionnaire d’événement vide qui était une fonction ne pourra pas toujours être
correct. Pour cette raison, tous vos événements et leurs gestionnaires
d’événements associés doivent être des procédures.

Création d’événements 34-3


Qu’est-ce qu’un événement ?

Bien qu’un gestionnaire d’événement ne puisse pas être une fonction, vous
pouvez toujours obtenir les informations à partir du code du développeur de
l’application en utilisant les paramètres var. Lorsque vous effectuerez ceci,
vérifiez que vous affectez une valeur correcte au paramètre avant d’appeler le
gestionnaire afin de ne pas rendre obligatoire la modification de la valeur par le
code de l’utilisateur.
Un exemple de transmission des paramètres var à un gestionnaire d’événement
est fourni par l’événement OnKeyPress, de type TKeyPressEvent. TKeyPressEvent
définit deux paramètres, l’un indiquant l’objet ayant généré l’événement et
l’autre la touche enfoncée :
type
TKeyPressEvent = procedure (Sender: TObject; var Key: Char) of object;
Normalement, le paramètre Key contient le caractère tapé par l’utilisateur.
Toutefois dans certaines circonstances, l’utilisateur de composant peut souhaiter
changer ce caractère. Par exemple, pour forcer tous les caractères en majuscules
dans un éditeur. Dans un cas comme celui-là, l’utilisateur doit définir le
gestionnaire suivant pour gérer les frappes de touches :
procedure TForm1.Edit1KeyPressed(Sender: TObject; var Key: Char);
begin
Key := UpCase(Key);
end;
Vous pouvez également utiliser les paramètres var pour permettre à l’utilisateur
de surcharger la gestion par défaut.

Les gestionnaires d’événements sont facultatifs


Lorsque vous créez des composants, n’oubliez pas que les développeurs qui les
utilisent ne vont pas forcément leur associer des gestionnaires. Cela signifie que
l’exécution de vos composants ne doit pas échouer ni générer d’erreur parce que
l’utilisateur n’a pas associé un gestionnaire à un événement. Le mécanisme pour
appeler un gestionnaire et gérer les événements, alors que l’utilisateur n’a pas
associé de gestionnaire, est décrit dans “Appel de l’événement” à la page 34-9.
Des événements se produisent en permanence dans une application Windows. Le
simple fait de déplacer le pointeur de la souris sur un composant visuel provoque
l’émission par Windows de nombreux messages de déplacement de la souris que le
composant traduit en événements OnMouseMove. Dans la plupart des cas, les
développeurs ne veulent pas gérer les événements déplacement de souris et il ne
faut pas que cela pose un problème. Aussi, les composants que vous créez doivent
posséder des gestionnaires pour ces événements.
Mais surtout, les développeurs d’applications peuvent écrire le code qu’ils
veulent dans un gestionnaire d’événement. Les gestionnaires d’événements des
composants de la VCL sont écrits de façon à minimiser le risque de génération
d’erreurs. Evidemment, vous ne pouvez pas empêcher les erreurs de logique
dans le code de l’application, mais vous pouvez vous assurer que les structures
de données sont initialisées avant que les événements ne soient appelés, de sorte
que les développeurs ne tentent pas d’accéder à des données incorrectes.

34-4 Guide du développeur


Implémentation des événements standard

Implémentation des événements standard


Les contrôles fournis avec Delphi héritent des événements correspondant aux
occurrences les plus courantes de Windows. Ces événements sont appelés
événements standard. Bien que tous ces événements soient intégrés aux contrôles
standard, ils sont souvent déclarés protected, pour que les développeurs ne
puissent pas leur associer de gestionnaire. Lorsque vous créez un contrôle, vous
pouvez choisir de rendre visibles certains événements aux utilisateurs de votre
contrôle.
Les trois points suivants sont à prendre en compte pour incorporer des
événements standard dans vos contrôles :
• Identification des événements standard
• Rendre visibles des événements
• Changement de la gestion des événements standard

Identification des événements standard


Il existe deux catégories d’événements standard : ceux définis pour tous les
contrôles et ceux définis uniquement pour les contrôles fenêtrés standard.

Evénements standard pour tous les contrôles


Les événements de base sont définis dans la classe TControl. Tous les contrôles,
qu’ils soient fenêtrés, graphiques ou personnalisés, héritent de ces événements. La
liste suivante donne tous les événements disponibles pour l’ensemble des contrôles :

OnClick OnDragDrop OnEndDrag OnMouseMove


OnDblClick OnDragOver OnMouseDown OnMouseUp

Les événements standard disposent de méthodes virtuelles protégées, déclarées


dans TControl, dont les noms correspondent aux noms des événements. Par
exemple, les événements OnClick appellent une méthode nommée Click, et les
événements OnEndDrag appellent une méthode nommée DoEndDrag.

Evénements standard pour les contrôles standard


Outre les événements communs à tous les contrôles, les contrôles fenêtrés standard
(ceux descendant de TWinControl) disposent des événements suivants :

OnEnter OnKeyDown OnKeyPress


OnKeyUp OnExit

Comme les événements standard de TControl, les événements des contrôles fenêtrés
disposent de méthodes correspondantes.

Création d’événements 34-5


Implémentation des événements standard

Rendre visibles des événements


Les événements standard de TControl et TWinControl sont déclarés protected, de
même que les méthodes correspondantes. Si vous héritez de l’une de ces classes
abstraites et voulez rendre leurs événements accessibles à l’exécution ou pendant
la conception, vous devez redéclarer les événements soit public, soit published.
La redéclaration d’une propriété sans spécifier d’implémentation conserve les
mêmes méthodes d’implémentation en ne modifiant que leur niveau de protection.
Vous pouvez donc prendre en compte un événement défini dans TControl mais
non visible, et le rendre visible en le déclarant public ou published.
Par exemple, pour créer un composant qui rende visible en mode conception
l’événement OnClick, ajoutez les lignes suivantes à la déclaration de classe du
composant.
type
TMyControl = class(TCustomControl)
ƒ
published
property OnClick;
end;

Changement de la gestion des événements standard


Pour modifier la façon dont votre composant répond à un certain type
d’événement, vous pouvez être tenté d’écrire un fragment de code pour l’associer à
l’événement en question. C’est précisément ce que ferait le développeur
d’applications. Mais lorsque vous créez un composant, vous devez faire en sorte
que l’événement reste disponible pour les développeurs qui vont utiliser le
composant.
C’est ce qui justifie les méthodes protégées de l’implémentation associées à chaque
événement standard. En surchargeant la méthode d’implémentation, vous pouvez
modifier la gestion interne de l’événement et en appelant la méthode reçue en
héritage, vous préservez la gestion standard, y compris la gestion de l’événement
par le code du développeur d’applications.
L’ordre dans lequel vous appelez les méthodes est significatif. En règle générale,
vous appelez d’abord la méthode héritée pour que le gestionnaire d’événement du
développeur d’applications s’exécute avant vos modifications (et parfois, empêche
l’exécution de vos modifications). Toutefois, dans certaines situations, vous voulez
exécuter votre code avant d’appeler la méthode héritée. Par exemple, si le code reçu
en héritage dépend d’une façon ou d’une autre de l’état de composant et si votre
code agit sur cet état, vous devez d’abord effectuer ces changements d’état avant
d’autoriser le code de l’utilisateur à y répondre.

34-6 Guide du développeur


Définition de vos propres événements

Supposons que vous écrivez un composant et que vous souhaitez modifier la façon
dont il répond aux clics de souris. Au lieu d’associer un gestionnaire à l’événement
OnClick, comme le ferait le développeur d’applications, surchargez la méthode
protégée Click :
procedure click override { déclaration forward }
ƒ
procedure TMyControl.Click;
begin
inherited Click; { exécute la gestion standard, y compris l’appel au gestionnaire}
... { vos modifications s’insèrent ici}
end;

Définition de vos propres événements


Il est relativement rare de définir des événements entièrement nouveaux. Toutefois,
il peut arriver qu’un comportement complètement différent soit introduit par un
composant et il faut alors lui définir un événement.
Voici les étapes qui interviennent dans la définition d’un événement :
• Déclenchement de l’événement
• Définition du type de gestionnaire
• Déclaration de l’événement
• Appel de l’événement

Déclenchement de l’événement
Vous avez besoin de savoir ce qui a déclenché l’événement. Pour certains
événements, la réponse est évidente. Par exemple, un événement associé à
l’enfoncement du bouton de souris se produit lorsque l’utilisateur clique avec le
bouton gauche de la souris provoquant l’envoi par Windows d’un message
WM_LBUTTONDOWN à l’application. La réception de ce message provoque l’appel
de la méthode MouseDown d’un composant qui à son tour appelle le code que
l’utilisateur a associé à l’événement OnMouseDown.
Néanmoins, certains événements sont liés de façon moins évidente à des
occurrences externes moins spécifiques. Par exemple, une barre de défilement
dispose d’un événement OnChange qui peut être déclenché par plusieurs
occurrences, telles des frappes de touche, des clics de souris, ou des modifications
dans d’autres contrôles. Lorsque vous définissez vos événements, assurez-vous que
les occurrences appellent tous les événements appropriés.

Deux sortes d’événements


Les deux sortes d’occurrences pour lesquelles vous pouvez être amené à définir des
événements sont les interactions utilisateur et les modifications d’état. Les
événements de type interaction utilisateur sont pratiquement toujours déclenchés
par un message issu de Windows indiquant que l’utilisateur a agi sur votre

Création d’événements 34-7


Définition de vos propres événements

composant d’une façon qui peut nécessiter une réponse de votre part. Les
événements de modification d’état peuvent aussi être le fait de messages issus de
Windows (par exemple, des changements de focalisation ou d’activation).
Cependant, ils peuvent également survenir à la suite d’une modification de
propriété ou de l’exécution d’une autre partie de code. Vous disposez d’un contrôle
total sur le déclenchement des événements que vous avez vous-même définis.
Définissez les événements avec soin de sorte que les développeurs soient
capables de les comprendre et de les utiliser.

Définition du type de gestionnaire


Après avoir détecté que l’un de vos événements s’est produit, vous devez définir la
façon de le gérer. Cela implique que vous devez déterminer le type du gestionnaire
d’événement. Dans la plupart des cas, les gestionnaires d’événements que vous
définissez vous-même seront des notifications simples ou spécifiques à des
événements particuliers. Il est également possible de récupérer de l’information en
provenance du gestionnaire.

Notifications simples
Un événement de type notification ne fait qu’indiquer qu’un événement particulier
s’est produit sans fournir aucune information sur le moment et l’endroit où il s’est
produit. Les notifications utilisent le type TNotifyEvent, qui véhiculent un
paramètre unique correspondant à l’émetteur de l’événement. Les seuls éléments
“connus” du gestionnaire associé à une notification sont donc le type d’événement
et le composant impliqué. Par exemple, les événements clic de souris sont des
notifications. Lorsque vous écrivez un gestionnaire pour un événement de ce type,
vous ne récupérez que deux informations : le fait qu’un clic s’est produit et le
composant impliqué.
Une notification est un processus à sens unique. Il n’existe aucun mécanisme
pour renvoyer une information en retour ou pour inhiber la gestion d’une
notification.

Gestionnaires d’événements spécifiques


Dans certains cas, savoir qu’un événement s’est produit et connaître le composant
impliqué n’est pas suffisant. Par exemple, si l’événement correspond à
l’enfoncement d’une touche, le gestionnaire voudra savoir quelle est cette touche.
Dans un cas comme celui-là, vous devez disposer d’un gestionnaire qui accepte des
paramètres pour ces informations supplémentaires.
Si votre événement a été généré en réponse à un message, les paramètres
transmis au gestionnaire d’événement seront vraisemblablement issus des
paramètres du message.

34-8 Guide du développeur


Définition de vos propres événements

Renvoi d’informations à partir du gestionnaire


Comme tous les gestionnaires d’événements sont des procédures, la seule façon
de renvoyer des informations à partir d’un gestionnaire consiste à faire appel à
un paramètre var. Vos composants peuvent utiliser les informations ainsi
récupérées pour déterminer le traitement éventuel d’un événement après
l’exécution du gestionnaire de l’utilisateur.
Par exemple, tous les événements liés aux touches (OnKeyDown, OnKeyUp et
OnKeyPress) transmettent par référence la valeur de la touche enfoncée dans un
paramètre intitulé Key. Le gestionnaire d’événement peut changer Key de façon à
donner l’impression à l’application qu’une touche différente est impliquée dans
l’événement. Cela permet par exemple de forcer en majuscules les caractères tapés.

Déclaration de l’événement
Une fois déterminé le type de votre gestionnaire d’événement, vous pouvez déclarer
le pointeur de méthode et la propriété pour l’événement. N’oubliez pas d’attribuer
un nom à l’événement qui soit à la fois significatif et descriptif pour que
l’utilisateur puisse comprendre son rôle. Dans la mesure du possible, choisissez des
noms de propriétés qui ressemblent à ceux de composants déjà définis.

Les noms d’événement débutent par “On”


Dans Delphi, les noms de la plupart des événements commencent par “On”. Il
s’agit d’une simple convention ; le compilateur n’impose pas cette restriction.
L’inspecteur d’objets détermine qu’une propriété est un événement en examinant le
type de la propriété : toutes les propriétés de type pointeur de méthode sont
interprétées comme des événements et apparaissent donc dans la page Evénements.
Les développeurs s’attendent à trouver les événements dans la liste alphabétique à
l’endroit des noms commençant par “On.” Vous risquez d’introduire une certaine
confusion en utilisant une autre convention.

Appel de l’événement
Il est préférable de centraliser tous les appels à un événement. Autrement dit, créez
une méthode virtuelle dans votre composant qui appelle le gestionnaire
d’événement de l’application (s’il a été défini) et qui fournit une gestion par défaut.
Le fait de rassembler tous les appels à un événement en un seul endroit vous
permet d’être sûr qu’un programmeur, qui dérive un nouveau composant à
partir du vôtre, pourra personnaliser la gestion de l’événement en surchargeant
cette méthode sans avoir à parcourir votre code pour repérer les endroits où
l’événement est appelé.
Deux autres considérations sont à prendre en compte concernant l’appel de
l’événement :
• Les gestionnaires vides doivent être valides.
• Les utilisateurs peuvent surcharger la gestion par défaut.

Création d’événements 34-9


Définition de vos propres événements

Les gestionnaires vides doivent être valides


Vous ne devez jamais créer une situation dans laquelle un gestionnaire d’événement
vide provoque une erreur, ou dans laquelle le bon fonctionnement d’un composant
dépend d’une réponse spécifique provenant du code de gestion d’un événement
dans l’application.
Un gestionnaire vide doit produire le même effet qu’un gestionnaire absent. Aussi,
le code pour appeler le gestionnaire d’événement dans une application doit
ressembler à ceci :
if Assigned(OnClick) then OnClick(Self);
... { exécute la gestion par défaut }
Il ne doit en aucun cas ressembler à ceci :
if Assigned(OnClick) then OnClick(Self)
else { exécute la gestion par défaut};

Les utilisateurs peuvent surcharger la gestion par défaut


Pour certains types d’événements, les développeurs peuvent vouloir remplacer la
gestion par défaut ou même supprimer l’ensemble des réponses. Pour permettre
cela, vous devez transmettre au gestionnaire un argument par référence et vérifier
si le gestionnaire renvoie une certaine valeur.
Cela reste dans la lignée de l’affirmation qu’un gestionnaire vide doit produire le
même effet qu’un gestionnaire absent : puisqu’un gestionnaire vide ne modifie en
rien la valeur des arguments passés par référence, la gestion par défaut se déroule
toujours après l’appel du gestionnaire vide.
Par exemple, lors de la gestion des événements frappe de touches, le
développeur d’applications peut omettre la gestion par défaut de la frappe de
touches du composant en attribuant le caractère null (#0) au paramètre var Key.
La logique de programmation sous-jacente est la suivante :
if Assigned(OnKeyPress) then OnKeyPress(Self, Key);
if Key <> #0 then ... { exécute la gestion par défaut}
Le code réel est légèrement différent car il prend également en compte les messages
Windows mais la logique reste la même. Par défaut, le composant appelle le
gestionnaire défini par l’utilisateur avant d’exécuter la gestion standard. Si le
gestionnaire défini par l’utilisateur attribue le caractère null à Key, le composant
omet l’exécution de la gestion par défaut.

34-10 Guide du développeur


Chapitre

35
Création de méthodes
Chapter 35

Les méthodes des composants sont des procédures et des fonctions intégrées
dans la structure d’une classe. Il n’existe pratiquement aucune restriction sur ce
que peuvent réaliser les méthodes d’un composant, mais Delphi n’en respecte
pas moins un certain nombre de standards qu’il est préférable de suivre. Ce
sont :
• Eviter les interdépendances
• Noms des méthodes
• Protection des méthodes
• Rendre virtuelles des méthodes
• Déclaration des méthodes
En général, les composants ne doivent pas contenir beaucoup de méthodes et
vous devez chercher à minimiser le nombre des méthodes appelées par une
application. Il est préférable d’encapsuler sous la forme de propriétés des
caractéristiques qu’il serait tentant d’implémenter sous forme de méthodes. Les
propriétés fournissent une interface qui s’inscrit parfaitement dans
l’environnement Delphi et sont accessibles au moment de la conception.

Eviter les interdépendances


Lorsque vous écrivez un composant, vous devez réduire au minimum les conditions
préalables imposées aux développeurs. Dans toute la mesure du possible, les
développeurs doivent pouvoir faire ce qu’ils veulent de votre composant, et à tout
moment. Il existe des situations où vous ne pourrez répondre à cette exigence mais
le but n’en demeure pas moins de s’en approcher au plus près.
La liste suivante donne quelques indications sur ce qu’il faut éviter :
• Les méthodes qu’un utilisateur doit obligatoirement appeler pour utiliser un
composant.

Création de méthodes 35-1


Noms des méthodes

• Les méthodes qui doivent s’exécuter selon un ordre défini.


• Les méthodes qui placent le composant dans un état ou un mode pour lequel
certains événements ou certaines méthodes deviennent incorrectes.
La meilleure façon de gérer les situations de ce type est de fournir le moyen
d’en sortir. Par exemple, si l’appel d’une méthode a pour effet de placer votre
composant dans un état où l’appel d’une autre méthode s’avère incorrect, vous
devez modifier cette seconde méthode de telle manière que si elle est appelée
alors que le composant se trouve dans un état impropre, elle corrige cet état
avant d’exécuter son propre code principal. Faute de mieux, vous devrez
déclencher une exception si l’utilisateur appelle une méthode non valide.
En d’autres termes, si vous générez une situation dans laquelle il existe des
interdépendances entre certaines parties de votre code, il est de votre
responsabilité de vous assurer qu’une utilisation incorrecte du code n’engendre
pas de problème. Un message d’avertissement, par exemple, est préférable à une
fin d’exécution anormale si l’utilisateur n’a pas respecté ces interdépendances.

Noms des méthodes


Delphi n’impose aucune restriction quant à la façon de nommer les méthodes et
leurs paramètres. Toutefois, certaines conventions facilitent l’exploitation des
méthodes par les développeurs d’applications. Souvenez-vous que l’architecture
même d’un composant a son influence sur les différents types de personnes qui
pourront utiliser ce composant.
Si vous avez l’habitude d’écrire du code qui ne s’adresse qu’à un nombre
restreint de programmeurs, vous ne vous êtes sans doute jamais interrogé sur le
choix du nom des entités que vous manipulez. Il est souhaitable de choisir des
noms compréhensibles car vos composants s’adressent à tous, y compris à ceux
qui ne connaissent pas bien votre code (ou qui maîtrisent imparfaitement la
programmation).
Voici quelques suggestions pour définir des noms de méthode compréhensibles :
• Choisissez des noms descriptifs. Utilisez des verbes d’action
• Un nom tel que CollerPressepapiers est plus explicite que Coller ou CP.
• Les noms de fonctions doivent refléter la nature de ce qu’elles renvoient.
• Bien qu’il puisse paraître évident, à vous programmeur, que le rôle d’une
fonction intitulée X soit de renvoyer la coordonnée horizontale d’un élément,
un nom tel que ObtenirPositionHorizontale sera compris par tout le monde.
Comme dernière considération, assurez-vous que votre méthode ait réellement
besoin d’être créée comme telle. Que le nom de votre méthode puisse être un
verbe est un bon repère. Si ce n’est pas le cas, demandez-vous s’il ne serait pas
préférable de transformer votre méthode en propriété.

35-2 Guide du développeur


Protection des méthodes

Protection des méthodes


Toutes les parties des classes, y compris les champs, les méthodes et les
propriétés, ont différents niveaux de protection ou de “visibilité”, comme
l’explique “Contrôle des accès” à la page 32-4. Il est facile de choisir le niveau
qui convient.
La plupart des méthodes écrites dans vos composants sont publiques ou
protégées. Il n’y a généralement pas lieu de déclarer une méthode private, à
moins qu’elle soit réellement spécifique à ce type de composant, au point que
même les composants dérivés ne peuvent pas y accéder.

Méthodes qui doivent être publiques


Toutes les méthodes qui peuvent être appelées par les développeurs
d’applications doivent être déclarées public. N’oubliez pas que la plupart des
appels aux méthodes ont lieu dans les gestionnaires d’événements, aussi les
méthodes doivent éviter de gaspiller les ressources système ou de placer
Windows dans un état où il n’est plus en mesure de répondre à l’utilisateur.
Remarque Les constructeurs et destructeurs sont toujours déclarés public.

Méthodes qui doivent être protégées


Toute méthode d’implémentation d’un composant doit être déclarée protected
afin d’empêcher les applications de les appeler à un moment inopportun. Si vous
avez défini des méthodes qui doivent demeurer inaccessibles au code, tout en
restant accessibles aux classes dérivées, vous devez les déclarer protected.
Par exemple, supposons une méthode dont l’exécution dépend de l’initialisation
préalable d’une donnée. Si cette méthode est déclarée publique, il peut arriver
que les applications tentent de l’appeler avant l’initialisation de la donnée. Mais,
en la déclarant protected, les applications ne peuvent le faire directement. Vous
pouvez alors définir d’autres méthodes publiques qui se chargent d’initialiser la
donnée avant d’appeler la méthode protected.
Les méthodes d’implémentation des propriétés doivent être déclarées comme
virtuelles et protected. Les méthodes ainsi déclarées permettent aux
développeurs d’applications de surcharger l’implémentation des propriétés,
augmentant leurs fonctionalités ou les remplaçant complètement. De telles
propriétés sont complètement polymorphes. Instaurer un accès protected à ces
méthodes garantit que les développeurs ne pourront pas les appeler par accident
ni modifier la propriété par inadvertance.

Création de méthodes 35-3


Rendre virtuelles des méthodes

Méthodes abstraites
Une méthode est parfois déclarée abstract dans un composant Delphi. Dans la
VCL, les méthodes abstraites se produisent habituellement dans les classes dont
les noms commencent par “custom”, comme dansTCustomGrid. De telles classes
sont elles-mêmes abstraites, au sens où elles ne servent qu’à la dérivation de
classes descendantes.
Bien que vous puissiez créer un objet instance d’une classe contenant un membre
abstrait, ce n’est pas recommandé. L’appel du membre abstrait entraîne une
exception EAbstractError.
La directive abstract est utilisée pour indiquer des parties de classes qui doivent
être surfacées et définies dans des composants descendants ; cela force les
écrivains de composants à redéclarer le membre abstrait dans des classes
descendantes avant que des instances actuelles de la classe puissent être créées.

Rendre virtuelles des méthodes


Vous rendrez virtuelles les méthodes lorsque vous souhaitez que des types
différents puissent exécuter des codes différents en réponse au même appel de
méthode.
Si vous créez des composants pour qu’ils soient exploitables par les
développeurs d’applications directement, vous voudrez probablement rendre non
virtuelles vos méthodes. D’autre part, si vous créez des composants abstraits
desquels d’autres composants vont dériver, vous devez envisager de rendre
virtuelles les méthodes ajoutées. De cette façon, les composants dérivés pourront
surcharger les méthodes virtuelles reçues en héritage.

Déclaration des méthodes


La déclaration d’une méthode dans un composant ne diffère en rien de celle
d’une méthode d’une autre classe.
Pour déclarer une nouvelle méthode dans un composant, vous devez :
• Ajouter la déclaration à la déclaration de type du composant dans le fichier
en-tête de ce dernier.
• Implémenter la méthode dans la partie implementation de l’unité du composant.
Le code suivant montre un composant qui définit deux nouvelles méthodes,
l’une est déclarée protected static et l’autre public et virtual.
type
TSampleComponent = class(TControl)
protected
procedure MakeBigger; { déclare la méthode protected static }

35-4 Guide du développeur


Déclaration des méthodes

public
function CalculateArea: Integer; virtual; { déclare la méthode public virtual }
end;
ƒ
implementation
ƒ
procedure TSampleComponent.MakeBigger; { implémente la première méthode}
begin
Height := Height + 5;
Width := Width + 5;
end;
function TSampleComponent.CalculateArea: Integer; { implémente la deuxième méthode}
begin
Result := Width * Height;
end;

Création de méthodes 35-5


35-6 Guide du développeur
Chapitre

Graphiques et composants
Chapter 36
36
Windows fournit une puissante interface GDI (Graphics Device Interface) servant
à dessiner des graphiques indépendamment des périphériques.
Malheureusement, GDI impose au programmeur des contraintes supplémentaires
telles que la gestion des ressources graphiques. Delphi prend en charge toutes
ces tâches GDI ingrates, vous laisse vous concentrer sur le travail productif, vous
épargnant les recherches de handles perdus ou de ressources non restituées.
De même que toute partie de l’API Windows, vous pouvez appeler les fonctions
GDI directement depuis votre application Delphi. Toutefois, vous vous rendrez
vite compte que l’utilisation de l’encapsulation Delphi des fonctions graphiques
est un moyen plus efficace et plus rapide de créer des graphiques.
Les rubriques de cette section comprennent :
• Présentation des graphiques
• Utilisation du canevas
• Travail sur les images
• Bitmaps hors écran
• Réponse aux changements

Présentation des graphiques


Delphi encapsule à différents niveaux les fonctions GDI de Windows. Le
programmeur qui écrit des composants doit comprendre comment ceux-ci
affichent leurs images à l’écran. Lorsque vous appelez directement les fonctions
GDI, vous devez disposer d’un handle sur un contexte de périphérique dans
lequel vous avez sélectionné des outils de dessin comme les crayons, les
pinceaux et les fontes. Après avoir tracé vos images, vous devez remettre le
contexte de périphérique dans son état initial avant de le restituer.

Graphiques et composants 36-1


Présentation des graphiques

Pour vous épargner la gestion de vos graphiques à un niveau détaillé, Delphi


fournit une interface à la fois simple et complète : il s’agit de la propriété Canvas
des composants. Le canevas garantit qu’un contexte de périphérique valide est
disponible et restitue le contexte lorsqu’il est inutilisé. Il dispose également de
propriétés spécifiques pour représenter la fonte, le crayon et le pinceau en cours.
Le canevas gère toutes ces ressources à votre place et vous n’avez donc pas le
souci de créer, de sélectionner ou de restituer les éléments comme le handle d’un
crayon. Vous n’avez qu’à indiquer au canevas le crayon à utiliser et il se charge
du reste.
L’un des avantages de laisser Delphi gérer les ressources graphiques à votre
place est que Delphi peut mettre en mémoire cache les ressources en vue d’un
usage ultérieur, ce qui accélère considérablement les opérations répétitives. Par
exemple, supposons qu’un programme crée, utilise puis restitue un outil crayon
d’un certain type plusieurs fois de suite, vous devez répéter ces étapes chaque
fois que vous l’utilisez. Comme Delphi stocke en mémoire cache les ressources
graphiques, il y a une forte probabilité qu’un outil que vous utilisez de façon
répétitive se trouve dans la mémoire cache. Aussi, au lieu de recréer l’outil en
question, Delphi se contentera de réutiliser celui qui existe déjà.
Par exemple, vous pouvez imaginer une application avec des dizaines de fiches
ouvertes et des centaines de contrôles. Chacun de ces contrôles peut présenter
une ou plusieurs propriétés TFont. Comme cela peut générer des centaines ou
des milliers d’instances d’objets TFont, la plupart des applications n’utiliseront
que deux ou trois handles de fontes grâce au cache de fontes VCL.
Voici deux exemples montrant à quel point le code Delphi manipulant des
graphiques peut être simple. Le premier, extrait d’une application écrite avec
ObjectWindows, utilise les fonctions GDI standard pour dessiner une ellipse
jaune bordée de bleu. Le second utilise un canevas pour dessiner la même ellipse
dans une application écrite avec Delphi.
procedure TMyWindow.Paint(PaintDC: HDC; var PaintInfo: TPaintStruct);
var
PenHandle, OldPenHandle: HPEN;
BrushHandle, OldBrushHandle: HBRUSH;
begin
PenHandle := CreatePen(PS_SOLID, 1, RGB(0, 0, 255)); { crée un crayon bleu}
OldPenHandle := SelectObject(PaintDC, PenHandle); { dit à DC d’utiliser le crayon bleu }
BrushHandle := CreateSolidBrush(RGB(255, 255, 0)); { crée un pinceau jaune}
OldBrushHandle := SelectObject(PaintDC, BrushHandle); { dit à DC d’utiliser le pinceau
// jaune}
Ellipse(HDC, 10, 10, 50, 50); { dessine l’ellipse }
SelectObject(OldBrushHandle); { restaure le pinceau original }
DeleteObject(BrushHandle); { supprime le pinceau jaune}
SelectObject(OldPenHandle); { restaure le crayon original}
DeleteObject(PenHandle); { détruit le crayon bleu}
end;

36-2 Guide du développeur


Utilisation du canevas

procedure TForm1.FormPaint(Sender: TObject);


begin
with Canvas do
begin
Pen.Color := clBlue; { crée le crayon bleu}
Brush.Color := clYellow; { crée le pinceau jaune}
Ellipse(10, 10, 50, 50); { trace l’ellipse }
end;
end;

Utilisation du canevas
La classe canevas encapsule les graphiques Windows à plusieurs niveaux, allant
des fonctions de haut niveau (pour dessiner des lignes, des formes et du texte)
aux accès GDI de bas niveau, en passant par les propriétés de niveau
intermédiaire, pour manipuler les moyens de dessin du canevas.
Le tableau suivant résume les possibilités du canevas.

Tableau 36.1 Résumé des possibilités du canevas


Niveau Opération Outils
Haut Dessin de lignes et de formes Méthodes comme MoveTo, LineTo,
Rectangle et Ellipse
Affichage et mesure de texte Méthodes TextOut, TextHeight, TextWidth
et TextRect
Remplissage de zones Méthodes FillRect et FloodFill
Intermédiaire Personnalisation de texte et des Propriétés Pen, Brush et Font
graphiques
Manipulation de pixels Propriété Pixels.
Copie et fusion d’images Méthodes Draw, StretchDraw, BrushCopy
et CopyRect ; propriété CopyMode
Bas Appel des fonctions GDI de Propriété Handle
Windows

Pour plus d’informations sur les classes canevas, leurs méthodes et leurs
propriétés, reportez-vous à l’aide en ligne.

Travail sur les images


Dans Delphi, la part la plus importante de votre travail sur les graphiques se
limitera au dessin direct sur le canevas des composants et des fiches. Mais
Delphi fournit également les moyens de gérer les images graphiques
indépendantes, comme les bitmaps, les métafichiers et les icônes, en assurant
dans le même temps la gestion automatique des palettes.

Graphiques et composants 36-3


Travail sur les images

Les trois sujets suivants sont nécessaires à la compréhension du travail sur les
images dans Delphi :
• Utilisation d’une image, d’un graphique ou d’un canevas
• Chargement et stockage des graphiques
• Gestion des palettes

Utilisation d’une image, d’un graphique ou d’un canevas


Il existe trois sortes de classes dans Delphi intervenant sur les graphiques :
• Un canevas représente une surface de dessin point par point dans une fiche,
un contrôle graphique, une imprimante ou un bitmap. Un canevas est toujours
une propriété de quelque chose d’autre, jamais une classe autonome.
• Un graphique représente une image graphique telle qu’elle se trouve dans un
fichier ou une ressource, comme un bitmap, une icône ou un métafichier.
Delphi définit les classes TBitmap, TIcon et TMetafile, toutes descendants de
l’objet générique TGraphic. Vous pouvez aussi définir vos propres classes
graphiques. En définissant une interface standard minimale pour tous les
graphiques, TGraphic fournit un mécanisme simple destiné aux applications
pour qu’elles puissent exploiter facilement les différentes sortes de graphiques
disponibles.
• Une image est le conteneur d’un graphique, elle peut donc contenir n’importe
quelle classe graphique. Autrement dit, un élément de type TPicture peut
contenir un bitmap, une icône, un métafichier, un type de graphique défini
par l’utilisateur, et l’application y accède d’une seule façon par l’intermédiaire
de la classe image. Par exemple, un contrôle image dispose d’une propriété
Picture, de type TPicture, qui le rend capable d’afficher les images de plusieurs
sortes de graphiques.
Souvenez-vous qu’une classe image a toujours un graphique et qu’un graphique
peut avoir un canevas. Le seul graphique standard ayant un canevas est TBitmap.
Normalement, lorsque vous avez affaire à une image, vous travaillez uniquement
avec les constituants de la classe graphique exposés via TPicture. Si vous voulez
accéder aux constituants spécifiques de la classe graphique elle-même, vous
pouvez faire référence à la propriété Graphic de l’image.

Chargement et stockage des graphiques


Toutes les images et tous les graphiques de Delphi peuvent charger leurs images
à partir d’un fichier et les stocker en retour dans ce même fichier (ou dans un
autre). Ceci peut s’effectuer à tout moment.
Pour charger une image dans un objet image à partir d’un fichier, appelez la
méthode LoadFromFile de l’objet image.
Pour sauvegarder une image dans un fichier à partir d’un objet image, appelez
la méthode SaveToFile de l’objet image.

36-4 Guide du développeur


Travail sur les images

LoadFromFile et SaveToFile acceptent le nom d’un fichier comme seul paramètre.


LoadFromFile utilise l’extension du fichier pour déterminer le type d’objet
graphique créé ou chargé. SaveToFile utilise le type de fichier approprié au type
d’objet graphique enregistré.
Pour charger un bitmap dans l’objet image d’un contrôle image, par exemple,
vous devez transmettre le nom du fichier bitmap à la méthode LoadFromFile de
l’objet image :
procedure TForm1.LoadBitmapClick(Sender: TObject);
begin
Image1.Picture.LoadFromFile('RANDOM.BMP');
end;
L’objet image reconnaît .BMP comme extension par défaut des fichiers bitmap,
elle crée donc le graphique en tant que TBitmap, avant d’appeler la méthode
LoadFromFile du graphique. Puisque le graphique est un bitmap, l’image est
chargée depuis le fichier en tant que bitmap.

Gestion des palettes


Lorsque l’exécution fait intervenir un périphérique supportant les palettes
(typiquement un mode vidéo 256 couleurs), Delphi assure automatiquement la
réalisation des palettes. Si un contrôle dispose d’une palette, vous pouvez utiliser
deux méthodes héritées de TControl pour contrôler la façon dont Windows prend
en compte la palette.
Les points suivants sont à prendre en compte lorsque vous travaillez avec des
palettes :
• Spécification d’une palette pour un contrôle
• Réponse aux changements de palette
La plupart des contrôles n’ont pas besoin de palette. Toutefois, dans le cas des
contrôles contenant des images graphiques riches en couleurs (tels que le
contrôle image) une interaction entre Windows et le pilote de périphérique écran
peut être nécessaire pour afficher correctement le contrôle. Dans Windows, ce
processus est appelé réalisation de palettes.
La réalisation de palettes est le processus mis en œuvre pour que la fenêtre en
avant-plan utilise la totalité de la palette qui lui est associée. Quant aux fenêtres
en arrière-plan, elles exploitent autant que possible les couleurs de leurs palettes
propres, l’approximation des couleurs manquantes se faisant en prenant la
couleur disponible la plus proche dans la palette “réelle”. Windows effectue un
travail permanent de réalisation des palettes au fur et à mesure que les fenêtres
sont déplacées d’avant en arrière-plan.
Remarque Delphi n’assure ni la création, ni la maintenance des palettes autres que celles
des bitmaps. Toutefois, si vous disposez d’un handle de palette, les contrôles
Delphi peuvent se charger de sa gestion.

Graphiques et composants 36-5


Bitmaps hors écran

Spécification d’une palette pour un contrôle


Pour spécifier la palette d’un contrôle, vous devez surcharger la méthode
GetPalette du contrôle pour qu’elle renvoie le handle de la palette.
Le fait de spécifier la palette d’un contrôle a deux conséquences pour votre
application :
• Cela informe l’application que la palette de votre contrôle doit être réalisée.
• Cela désigne la palette à utiliser pour cette réalisation.

Réponse aux changements de palette


Si votre contrôle spécifie une palette en surchargeant GetPalette, Delphi se
chargera de répondre automatiquement aux messages de palette en provenance
de Windows. La méthode qui gère les messages de palette est PaletteChanged.
Le rôle primaire de PaletteChanged est de déterminer s’il est nécessaire de réaliser
les palettes des contrôles en avant et arrière-plan. Windows gère la réalisation
des palettes en faisant en sorte que la fenêtre la plus en avant dispose de la
palette d’avant-plan, la résolution des couleurs des autres fenêtres se faisant à
l’aide des palettes d’arrière-plan. Delphi va même plus loin car il réalise les
palettes des contrôles d’une fenêtre en respectant l’ordre de tabulation. Seul cas
où vous voudrez peut-être redéfinir ce comportement : lorsqu’un contrôle, autre
que le premier dans l’ordre de tabulation, doit récupérer la palette d’avant-plan.

Bitmaps hors écran


Lorsque vous dessinez des images graphiques complexes, une technique de
programmation habituelle dans Windows consiste à créer d’abord un bitmap
hors écran, puis à dessiner l’image dans ce bitmap et à copier ensuite la totalité
de l’image du bitmap vers sa destination finale à l’écran. L’utilisation d’une
image hors-écran réduit le scintillement causé par un tracé répétitif à l’écran.
Dans Delphi, la classe bitmap qui représente les images point par point stockées
dans les ressources et les fichiers, peut également fonctionner en tant qu’image
hors écran.
Les points suivants sont à prendre en compte lorsque vous travaillez avec des
bitmaps hors écran :
• Création et gestion des bitmaps hors écran.
• Copie des images bitmap.

Création et gestion des bitmaps hors écran


Lorsque vous créez des images graphiques complexes, vous devez généralement
éviter de les dessiner directement sur le canevas qui apparaît à l’écran. Au lieu
de les dessiner sur le canevas d’une fiche ou d’un contrôle, vous devez plutôt
construire un objet bitmap puis dessiner sur son canevas avant de copier la
totalité de l’image sur le canevas de l’écran.

36-6 Guide du développeur


Réponse aux changements

La méthode Paint d’un contrôle graphique est un exemple d’utilisation typique


d’un bitmap hors écran. Comme avec tout objet temporaire, l’objet bitmap doit
être protégé par un bloc try..finally :
type
TFancyControl = class(TGraphicControl)
protected
procedure Paint; override ; { surcharge la méthode Paint }
end;
procedure TFancyControl.Paint;
var
Bitmap: TBitmap; { variable temporaire pour le bitmap hors écran }
begin
Bitmap := TBitmap.Create; { construit l’objet bitmap }
try
{ draw on the bitmap }
{ copy the result into the control's canvas }
finally
Bitmap.Free; { détruit l’objet bitmap }
end;
end;

Copie des images bitmap


Delphi fournit quatre moyens pour copier des images d’un canevas à un autre.
Selon l’effet recherché, vous pouvez appeler différentes méthodes.
Le tableau suivant résume les méthodes de copie d’image des objets canevas.

Tableau 36.2 Méthodes de copie des images


Pour créer cet effet Appelez cette méthode
Copie d’un graphique entier. Draw
Copie et redimensionnement d’un graphique. StretchDraw
Copie d’une partie d’un canevas. CopyRect
Copie d’un bitmap avec effets. BrushCopy

Réponse aux changements


Tous les objets graphiques, y compris les canevas et les objets dont ils sont
propriétaires (crayons, pinceaux et fontes), disposent d’événements intégrés pour
répondre aux changements. Grâce à ces événements, vous pouvez faire en sorte
que vos composants (ou les applications qui les utilisent) répondent aux
changements en redessinant leurs images.
S’agissant d’objets graphiques, la réponse aux changements est particulièrement
importante si ces objets sont publiés comme éléments accessibles de l’interface de
conception de vos composants. La seule façon d’être certain que l’aspect d’un
composant au moment de la conception corresponde aux propriétés définies dans
l’inspecteur d’objets consiste à répondre aux changements apportés aux objets.

Graphiques et composants 36-7


Réponse aux changements

Pour répondre aux modifications d’un objet graphique, vous devez associer une
méthode à l’événement OnChange de sa classe.
Le composant forme publie les propriétés représentant le crayon et le pinceau
qu’il utilise pour tracer sa forme. Le constructeur du composant associe une
méthode à l’événement OnChange de chacun, ce qui a pour effet de provoquer le
rafraîchissement de l’image du composant si le crayon ou le pinceau est modifié :
type
TShape = class(TGraphicControl)
public
procedure StyleChanged(Sender: TObject);
end;
ƒ
implementation
ƒ
constructor TShape.Create(AOwner: TComponent);
begin
inherited Create(AOwner); { appelle toujours le constructeur hérité ! }
Width := 65;
Height := 65;
FPen := TPen.Create; { construit le crayon}
FPen.OnChange := StyleChanged; { affecte la méthode à l’événement OnChange }
FBrush := TBrush.Create; { construit le pinceau}
FBrush.OnChange := StyleChanged; { affecte la méthode à l’événement OnChange }
end;
procedure TShape.StyleChanged(Sender: TObject);
begin
Invalidate(); { détruit et redessine le composant }
end;

36-8 Guide du développeur


Chapitre

Gestion des messages


Chapter 37
37
La gestion des messages envoyés par Windows aux applications est l’une des clés de
la programmation Windows traditionnelle. Delphi gère la plupart des messages
standard à votre place. Mais, vous aurez peut-être à gérer des messages que Delphi
ne prend pas en compte, ou encore à créer puis à gérer vos propres messages.
Il y a trois aspects à prendre en considération lorsque vous travaillez avec des
messages :
• Compréhension du système de gestion des messages
• Modification de la gestion des messages
• Création de nouveaux gestionnaires de messages

Compréhension du système de gestion des messages


Toutes les classes Delphi disposent d’un mécanisme intégré pour gérer les
messages : ce sont les méthodes de gestion des messages ou gestionnaires de messages.
L’idée sous-jacente aux gestionnaires de messages est la suivante : un objet reçoit
des messages qu’il répartit selon le message en appelant une méthode choisie dans
un ensemble de méthodes spécifiques. Un gestionnaire par défaut est appelé si
aucune méthode n’est définie pour le message.
Le diagramme suivant illustre le fonctionnement du système de répartition de
message :
Evénement MainWndProc WndProc Dispatch Gestionnaire

Gestion des messages 37-1


Compréhension du système de gestion des messages

La bibliothèque des composants visuels définit un système de répartition des


messages qui convertit tous les messages Windows (y compris ceux définis par
l’utilisateur) destinés à une classe spécifique en appels à des méthodes. Vous
n’aurez sans doute jamais besoin de modifier le mécanisme de répartition des
messages. En revanche, vous aurez à écrire des méthodes de gestion des messages.
Voir “Déclaration d’une nouvelle méthode de gestion d’un message” à la
page 37-7 pour plus de détails sur ce sujet.

Que contient un message Windows ?


Un message Windows est un enregistrement de données contenant plusieurs
données membres exploitables. Le plus important est celui qui contient une
valeur de la taille d’un entier identifiant le message. Windows définit de
nombreux messages et l’unité Messages déclare tous leurs identificateurs.
Un paramètre contient 16 bits, l’autre 32 bits. Vous voyez souvent du code
Windows qui fait référence à ces valeurs avec wParam et lParam, comme
“paramètre de type word” et “paramètre de type long.” Souvent, chaque
paramètre contient une information, qui fait référence à un mot de poids fort
dans le paramètre de type long.
A l’origine, un programmeur Windows devait mémoriser le contenu de chaque
paramètre ou consulter l’API Windows. Microsoft a récemment donné un nom
aux paramètres. Ces “décomposeurs de message” ainsi appelés simplifient la
compréhension des informations accompagnant chaque message. Par exemple, les
paramètres pour le message WM_KEYDOWN maintenant appelés nVirtKey et
lKeyData, donnent plus d’informations spécifiques que wParam et lParam.
Pour chaque type de message, Delphi définit un type d’enregistrement qui donne
un nom mnémonique à chaque paramètre. Les messages souris transmettent par
exemple les coordonnées x et y de l’événement souris dans le paramètre de type
long, une dans le mot de poids fort, et l’autre dans le mot de poids faible. Avec
l’utilisation de la structure souris-message, vous n’avez pas à vous soucier du
mot dont il s’agit, car vous faites référence aux paramètres par les noms XPos et
YPos au lieu de lParamLo et lParamHi.

Répartition des messages


Lorsqu’une application crée une fenêtre, elle recense une procédure fenêtre avec le
modèle Windows. La procédure fenêtre représente la routine qui gère les
messages pour la fenêtre. Habituellement, la procédure fenêtre contient une
instruction longue case avec des entrées pour chaque message devant être géré
par la fenêtre. N’oubliez pas que “fenêtre” dans ce sens signifie seulement
quelque chose sur l’écran : chaque fenêtre, chaque contrôle, etc. A chaque fois
que vous créez un nouveau type de fenêtre, vous devez créer une procédure
fenêtre complète.

37-2 Guide du développeur


Modification de la gestion des messages

Delphi simplifie la répartition des messages de plusieurs manières :


• Chaque composant hérite d’un système complet de répartition de message.
• Le système de répartition de message dispose d’une gestion par défaut. Vous
ne définissez de gestionnaire que pour les messages auxquels vous souhaitez
spécifiquement répondre.
• Vous pouvez modifier des parties de la gestion de message en vous appuyant
sur les méthodes reçues en héritage pour la majeure partie du traitement.
Le bénéfice le plus évident de cette répartition de message est le suivant : à tout
moment, vous pouvez envoyer n’importe quel message à n’importe quel
composant. Si le composant n’a pas de gestionnaire défini pour ce message, le
système de gestion par défaut s’en charge, généralement en ignorant le message.

Suivi du flux des messages


La méthode MainWndProc est recensée par Delphi comme procédure de fenêtre
pour tous les types de composants d’une application. MainWndProc contient un bloc
de gestion des exceptions qui transmet la structure du message en provenance de
Windows à la méthode virtuelle WndProc, gérant les exceptions éventuelles à l’aide
de la méthode HandleException de la classe application.
MainWndProc est une méthode non virtuelle qui n’effectue aucune gestion
particulière des messages. Cette gestion a lieu dans WndProc, chaque type de
composant ayant la possibilité de surcharger cette méthode pour répondre à ses
besoins spécifiques.
Les méthodes WndProc vérifient les conditions spéciales qui peuvent affecter le
traitement et “interceptent”, s’il le faut, les messages non souhaités. Par exemple,
lorsque vous faites glisser un composant, celui-ci ignore les événements du clavier, et
la méthode WndProc de TWinControl ne transmet ces événements que si l’utilisateur
ne fait pas glisser le composant. Enfin, WndProc appelle Dispatch, une méthode non
virtuelle héritée de TObject, qui détermine la méthode à appeler pour gérer le message.
Dispatch utilise la donnée membre Msg de l’enregistrement du message pour
déterminer comment répartir le message particulier. Si le composant définit un
gestionnaire pour ce message, Dispatch appelle cette méthode. Si aucun gestionnaire
n’est défini, Dispatch appelle DefaultHandler.

Modification de la gestion des messages


Avant de modifier la gestion des messages de vos composants, vous devez être
certain de ce que vous voulez effectivement faire. Delphi convertit la plupart des
messages en événements que l’auteur ou l’utilisateur du composant peut gérer. Plutôt
que de modifier le comportement définissant la gestion du message, vous voudrez
généralement modifier le comportement définissant la gestion de l’événement.
Pour modifier la gestion d’un message, vous devez surcharger la méthode qui gère
ce message. En outre, dans certaines circonstances, vous pouvez empêcher un
composant de gérer un message en interceptant ce message.

Gestion des messages 37-3


Modification de la gestion des messages

Surcharge de la méthode du gestionnaire


Pour modifier la façon dont un composant gère un message en particulier, vous
devez surcharger la méthode qui le gère. Si le composant ne gère pas le message en
question, vous devez déclarer une nouvelle méthode de gestion du message.
Pour surcharger la méthode de gestion d’un message, déclarez une nouvelle
méthode dans votre composant avec le même index de message que la méthode
surchargée. N’utilisez pas la directive override ; vous devez utiliser la directive
message et un index de message correspondant.
Remarquez qu’il n’est pas nécessaire que le nom de la méthode et le type du
paramètre var simple correspondent à la méthode surchargée. Seul l’index de
message est significatif. Pour plus de clarté, cependant, il est préférable de suivre
la convention d’appel des méthodes de gestion de message après les messages
qu’elles gèrent.
Par exemple, pour surcharger la gestion du message WM_PAINT d’un composant,
redéclarez la méthode WMPaint :
type
TMyComponent = class(...)
ƒ
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
end;

Utilisation des paramètres d’un message


Une fois l’intérieur d’une méthode de gestion de message, votre composant peut
accéder à tous les paramètres de la structure du message. Puisque le paramètre
passé au gestionnaire message est un paramètre var, le gestionnaire peut
modifier la valeur du paramètre si c’est nécessaire. Le seul paramètre qui change
fréquemment est le champ Result du message : il s’agit de la valeur renvoyée par
l’appel de SendMessage qui a émis le message.
Comme le type du paramètre Message transmis à la méthode de gestion dépend du
message géré, vous devez vous reporter à la documentation des messages Windows
pour connaître le nom et la signification de chaque paramètre. Si pour une raison
ou pour une autre, vous avez à vous référer aux paramètres d’un message en
utilisant l’ancienne convention d’appellation (WParam, LParam, etc.), vous devez
transtyper Message vers le type générique TMessage, qui utilise ces noms de
paramètres.

Interception des messages


Dans certaines circonstances, vous pouvez souhaiter que certains messages soient
ignorés par vos composants. Autrement dit, vous voulez empêcher le composant de
répartir un message à son gestionnaire. Pour intercepter un message de cette façon,
vous devez surcharger la méthode virtuelle WndProc.

37-4 Guide du développeur


Création de nouveaux gestionnaires de messages

La méthode WndProc sélectionne les messages avant de les transmettre à la


méthode Dispatch qui, à son tour, détermine la méthode qui gère le message. En
surchargeant WndProc, votre composant a la possibilité de filtrer certains messages
avant qu’ils ne soient transmis. La surcharge de WndProc pour un contrôle dérivé
de TWinControl ressemble à ceci :
procedure TMyControl.WndProc(var Message: TMessage);
begin
{ test pour déterminer la poursuite du traitement }
inherited WndProc(Message);
end;
Le composant TControl définit des plages entières de messages liés à la souris qu’il
filtre lorsqu’un utilisateur fait glisser puis lâche un contrôle. Une méthode WndProc
surchargée peut agir par deux moyens :
• Elle peut filtrer des plages entières de messages au lieu de spécifier un
gestionnaire pour chacun d’eux.
• Elle peut inhiber totalement la répartition des messages de façon à ce que les
gestionnaires ne soient jamais appelés.
Voici une partie de la méthode WndProc pour TControl, par exemple :
procedure TControl.WndProc(var Message: TMessage);
begin
ƒ
if (Message.Msg >= WM_MOUSEFIRST) and (Message.Msg <= WM_MOUSELAST) then
if Dragging then { gestion spécifique d’une opération glisser}
DragMouseMsg(TWMMouse(Message))
else
ƒ { gestion normale des autres opérations}
end;
ƒ { autrement gestion normale}
end;

Création de nouveaux gestionnaires de messages


Puisque Delphi fournit des gestionnaires pour la plupart des messages Windows
standard, vous avez à définir de nouveaux gestionnaires de message uniquement
lorsque vous définissez vous-mêmes vos propres messages. Pour travailler avec des
messages définis par l’utilisateur, nous allons étudier les deux points suivants :
• Définition de vos propres messages
• Déclaration d’une nouvelle méthode de gestion d’un message

Définition de vos propres messages


De nombreux composants standard définissent des messages pour leur usage
interne. Définir des messages peut servir à émettre des informations qui ne sont pas
prises en compte par les messages Windows standard ou à notifier un changement
d’état.

Gestion des messages 37-5


Création de nouveaux gestionnaires de messages

La définition d’un message est un processus à deux étapes :


1 Déclaration d’un identificateur de message
2 Déclaration d’un type enregistrement de message

Déclaration d’un identificateur de message


Un identificateur de message est une constante de la taille d’un entier. Windows se
réserve pour son propre usage les messages dont le numéro est inférieur à 1 024.
Lorsque vous déclarez vos propres messages, vous devez donc toujours débuter par
un numéro supérieur.
La constante WM_APP représente le numéro de départ pour les messages définis
par l’utilisateur. Lorsque vous définissez un identificateur de message, utilisez
WM_APP.
Notez que certains contrôles Windows standard utilisent des messages compris
dans la plage des messages utilisateur. Entre autres contrôles, il y a les boîtes liste,
les boîtes à options, les boîtes de saisie et les boutons de commande. Si vous
dérivez un composant à partir de l’un d’eux et si vous voulez lui associer un
nouveau message, vérifiez le contenu du l’unité Messages pour voir quels
messages Windows sont déjà définis pour ce contrôle.
Le code suivant définit deux messages utilisateur :
const
WM_MYFIRSTMESSAGE = WM_APP + 400;
WM_MYSECONDMESSAGE = WM_APP + 401;

Déclaration d’un type enregistrement de message


Si vous voulez attribuer un nom explicite aux paramètres de votre message, vous
devez déclarer un type enregistrement pour le message. L’enregistrement de
message correspond au type du paramètre transmis à la méthode de gestion du
message. Si vous n’utilisez pas les paramètres du message ou si vous souhaitez
utiliser l’ancienne notation (wParam, lParam, etc.), utilisez l’enregistrement de
message implicite, TMessage.
Pour déclarer un type enregistrement de message, respectez les conventions
suivantes :
1 Nommez le type enregistrement d’après le message en rajoutant le préfixe T à
son nom.
2 Donnez au premier champ de l’enregistrement le nom Msg et le type
TMsgParam.
3 Définissez les deux octets suivants pour qu’ils correspondent au paramètre Word,
et les deux suivants inutilisés.
ou
Définissez les quatre octets suivants pour qu’ils correspondent au paramètre
Longint.
4 Ajoutez un dernier champintitulé Result de type Longint.

37-6 Guide du développeur


Création de nouveaux gestionnaires de messages

Par exemple, voici un enregistrement de message pour tous les messages de


souris, TWMMouse, qui utilisent un enregistrement variant pour définir deux
ensembles de noms pour les mêmes paramètres.
type
TWMMouse = record
Msg: TMsgParam; ( en premier, l’identificateur du message )
Keys: Word; ( voici wParam )
case Integer of ( deux manières de voir lParam )
0: {
XPos: Integer; ( soit les coordonnées x et y...)
YPos: Integer);
1: {
Pos: TPoint; ( ... soit un simple point )
Result: Longint); ( et enfin, la donnée membre résultat)
end;

Déclaration d’une nouvelle méthode de gestion d’un message


Deux circonstances vous amènent à déclarer de nouvelles méthodes de gestion
des messages :
• Votre composant a besoin de gérer un message Windows qui n’est pas pris en
compte par les composants standard.
• Vous avez défini votre propre message et vous souhaitez l’utiliser avec vos
composants.
Pour déclarer la méthode de gestion d’un message, suivez les étapes ci-après :
1 Déclarez la méthode dans une partie protected de la déclaration de la classe du
composant.
2 Faites de la méthode une procédure.
3 Nommez la méthode suivant le message qu’elle gère en supprimant les
caractères de soulignement de son nom.
4 Transmettez un seul paramètre var appelé Message, du type défini par
l’enregistrement du message.
5 A l’intérieur de l’implémentation de la méthode message, écrivez le code de
gestion spécifique au composant.
6 Appelez le gestionnaire de message transmis en héritage.
Voici la déclaration, par exemple, d’un gestionnaire de message pour un message
utilisateur intitulé CM_CHANGECOLOR.
const
CM_CHANGECOLOR = WM_APP + 400;
type
TMyComponent = class(TControl)
ƒ

Gestion des messages 37-7


Création de nouveaux gestionnaires de messages

protected
procedure CMChangeColor(var Message: TMessage); message CM_CHANGECOLOR;
end;
procedure TMyComponent.CMChangeColor(var Message: TMessage);
begin
Color := Message.lParam;
inherited ;
end;

37-8 Guide du développeur


Chapitre

Accessibilité des composants


Chapter 38
38
au moment de la conception
Ce chapitre décrit les étapes permettant de rendre les composants que vous créez
accessibles dans l’EDI. Rendre vos composants accessibles à la conception est un
processus qui nécessite plusieurs étapes :
• Recensement des composants
• Ajout de bitmaps à la palette
• Fournir l’aide pour vos composants
• Ajout d’éditeurs de propriétés
• Ajout d’éditeurs de composants
• Compilation des composants en paquets
Toutes les étapes ne s’appliquent pas à tous les composants. Par exemple, si
vous ne définissez ni propriété ni événement nouveau, il n’est pas nécessaire de
fournir de l’aide. Le recensement et la compilation sont les seules étapes
obligatoires.
Une fois que vos composants ont été recensés et compilés en paquets, ils
peuvent être distribués à d’autres développeurs et installés dans l’EDI. Pour plus
d’informations sur l’installation des paquets dans l’EDI, voir “Installation de
paquets de composants” à la page 9-6.

Recensement des composants


Le recensement fonctionne en ayant l’unité de compilation comme base. Si vous
créez plusieurs composants dans la même unité de compilation, ils seront tous
recensés en même temps.
Pour recenser un composant, ajoutez une procédure Register àl’unité. Dans la
procédure Register, vous recensez les composants et déterminez leur
emplacement sur la palette des composants.

Accessibilité des composants au moment de la conception 38-1


Recensement des composants

Remarque Si vous créez votre composant en choisissant Composant|Nouveau Composant


dans l’EDI, le code requis pour recenser votre composant est automatiquement
ajouté.
Les étapes d’un recensement manuel de composant sont :
• Déclaration de la procédure Register
• Ecriture de la procédure Register

Déclaration de la procédure Register


Le recensement implique l’écriture d’une procédure unique dans l’unité, qui doit
être nommée Register. La procédure Register doit apparaître dans la partie
interface de l’unité.
Le code suivant montre la présentation d’une seule unité qui crée et recense des
nouveaux composants :
unit MyBtns;
interface
type
... { déclarez vos composants ici }
procedure Register; { ceci doit apparaître dans la section interface }
implementation
... { l’implémentation de composant vient ici}
procedure Register;
begin
... { recense les composants }
end;
end.
Dans la procédure Register, appelez RegisterComponents pour chaque composant
que vous souhaitez ajouter à la palette des composants. Si l’unité contient
plusieurs composants, vous pouvez les recenser en une seule fois.

Ecriture de la procédure Register


Dans la procédure Register d’une unité contenant des composants, vous devez
recenser chaque composant que vous voulez ajouter à la palette des composants.
Si l’unité contient plusieurs composants, vous pouvez les recenser en une seule
fois.
Pour recenser un composant, appelez la procédure RegisterComponents pour
chacune des pages de la palette auxquelles vous souhaitez ajouter des
composants. RegisterComponents nécessite trois informations importantes :
1 Spécification des composants
2 Spécification de la page de palette
3 Utilisation de la fonction RegisterComponents

38-2 Guide du développeur


Recensement des composants

Spécification des composants


Dans la procédure Register, transmettez les noms de composant dans un tableau
ouvert, que vous pouvez construire dans l’appel à RegisterComponents.
RegisterComponents('Miscellaneous', [TMyComponent]);
Vous pourriez aussi recenser plusieurs composants en même temps sur la même
page ou recenser des composants sur des pages différentes, comme dans le code
suivant :
procedure Register;
begin
RegisterComponents('Miscellaneous', [TFirst, TSecond]); { deux sur cette page... }
RegisterComponents('Assorted', [TThird]); { ...un sur une autre... }
RegisterComponents(LoadStr(srStandard), [TFourth]); { ...et un sur la page Standard }
end;

Spécification de la page de palette


Le nom de la page de palette est une chaîne. Si le nom que vous donnez pour la
page de palette n’existe pas, Delphi crée une nouvelle page avec ce nom. Delphi
stocke les noms des pages standard dans des ressources liste de chaînes afin que
les versions internationales du produit puissent nommer les pages dans leur
langue. Si vous voulez installer un composant dans l’une des pages standard,
vous obtiendrez la chaîne du nom de cette page en appelant la fonction LoadStr
et en transmettant la constante représentant la ressource chaîne de cette page
(par exemple srSystem pour la page Système).

Utilisation de la fonction RegisterComponents


Dans la procédure Register, appelez RegisterComponents pour recenser les
composants dans le tableau des classes. RegisterComponents est une fonction qui
présente trois paramètres : le nom de page de palette des composants, le tableau
des classes de composants et l’index de la dernière entrée du tableau.
Mettez le nom de la page sur la palette des composants, où les composants
doivent apparaître, dans le paramètre Page. Si la page nommée existe déjà, les
composants sont ajoutés à cette page. Si elle n’existe pas, Delphi crée une
nouvelle page palette ayant ce nom.
Appelez RegisterComponents depuis l’implémentation de la procédure Register
dans une des unités qui définit les composants personnalisés. Les unités
définissant les composants doivent alors être compilées en un paquet et ce
dernier doit être installé avant l’ajout des composants personnalisés à la palette
des composants.
procedure Register;
begin
RegisterComponents('System', [TSystem1, TSystem2]); {ajout à la page system}
RegisterComponents('MyCustomPage',[TCustom1, TCustom2]); { nouvelle page}
end;

Accessibilité des composants au moment de la conception 38-3


Ajout de bitmaps à la palette

Ajout de bitmaps à la palette


Chaque composant fait appel à un bitmap pour le représenter dans la palette. Si
vous ne spécifiez pas de bitmap, Delphi utilise un bitmap par défaut.
Puisque les bitmaps d’une palette ne sont nécessaires qu’à la conception, ils ne
sont pas compilés à l’intérieur de l’unité du composant. En revanche, ils doivent
être fournis dans un fichier de ressources Windows portant le même nom que
l’unité, mais avec l’extension .DCR (dynamic component resource). Vous pouvez
créer ce fichier de ressources en utilisant un éditeur d’images dans Delphi.
Chaque bitmap doit être un carré de 24 pixels de côté.
Pour chaque composant à installer, vous devez fournir un fichier de bitmaps, et
dans chaque fichier de bitmaps, un bitmap pour chaque composant recensé.
L’image bitmap a le même nom que le composant. Conservez le fichier de
bitmaps dans le même répertoire que les fichiers compilés, afin que Delphi
puisse localiser ces bitmaps lorsqu’il installe les composants dans la palette.
Par exemple, si vous créez un composant appelé TMyControl dans une unité
nommée ToolBox, vous devez créer un fichier de ressources appelé
TOOLBOX.DCR contenant un bitamp nommé TMYCONTROL. Les noms des
ressources ne tiennent pas compte des différences majuscules/minuscules, mais
sont en majuscules par convention.

Fournir l’aide pour vos composants


Lorsque vous sélectionnez un composant standard dans une fiche, ou une
propriété ou un événement dans l’inspecteur d’objets, vous pouvez appuyer sur
F1 pour obtenir de l’aide concernant cet élément. Les développeurs pourront
accéder au même type d’information sur vos composants si vous créez les
fichiers d’aide appropriés.
Vous pouvez fournir un fichier d’aide de faible encombrement pour décrire vos
composants et votre fichier d’aide devient partie intégrante du système d’aide
global de Delphi.
Voir “Création du fichier d’aide” à la page 38-4 pour des informations sur la
manière de composer le fichier d’aide à utiliser avec un composant.

Création du fichier d’aide


Vous pouvez utiliser l’outil de votre choix pour créer les fichiers d’aide
Windows (au format .rtf). Delphi inclut le Microsoft Help Workshop, qui compile
les fichiers d’aide et contient un guide en ligne destiné à l’auteur du système
d’aide. Vous y trouverez toutes les informations nécessaires à la création des
systèmes d’aide.

38-4 Guide du développeur


Fournir l’aide pour vos composants

La composition de fichiers d’aide pour les composants s’effectue en plusieurs


étapes :
• Création des entrées
• Aide contextuelle des composants
• Ajout des fichiers d’aide des composants

Création des entrées


Pour que l’aide associée à votre composant fonctionne de façon transparente avec
celle des autres composants de la bibliothèque, vous devez respecter les
conventions suivantes :
1 Chaque composant doit avoir une rubrique d’aide.
La rubrique associée au composant doit montrer dans quelle unité est déclaré
le composant et fournir une brève description du composant. La rubrique du
composant doit proposer des liens vers des fenêtres secondaires décrivant la
position du composant dans la hiérarchie des objets et répertorier l’ensemble
de ses propriétés, événements et méthodes. Les développeurs d’applications
accéderont à cette rubrique en sélectionnant le composant dans une fiche et en
appuyant sur F1. Pour avoir un exemple d’une rubrique associée à un
composant, positionnez-vous sur un composant quelconque dans une fiche et
appuyez sur F1.
La rubrique de composant doit avoir une note de bas de page # avec une
valeur unique de rubrique. La note de bas de page # identifie de manière
unique chaque rubrique du système d’aide.
La rubrique associée au composant doit avoir une note de bas de page “K”
(qui indique les mots clé de recherche) comprenant le nom de la classe du
composant. Par exemple, la note de bas de page des mots clés pour le
composant TMemo contient “TMemo”.
La rubrique associée au composant doit également comprendre une note de
bas de page $ qui indique le titre de la rubrique. Le titre apparaît dans la boîte
de dialogue des rubriques, la boîte de dialogue Signet et la fenêtre Historique.
2 Chaque composant doit inclure les rubriques de navigation secondaires
suivantes :
• Une rubrique de hiérarchie offrant des liens vers chaque ancêtre du
composant appartenant à sa hiérarchie.
• Une liste de toutes les propriétés disponibles dans le composant, avec des
liens vers les entrées décrivant ces propriétés.
• Une liste de tous les événements disponibles dans le composant, avec des
liens vers les entrées décrivant ces événements.
• Une liste de toutes les méthodes disponibles dans le composant, avec des
liens vers les entrées décrivant ces méthodes.

Accessibilité des composants au moment de la conception 38-5


Fournir l’aide pour vos composants

Les liens vers les classes d’objets, les propriétés ou les événements dans le
système d’aide de Delphi peuvent être réalisés à l’aide de Alinks. Alink opère
la liaison vers une classe d’objet en utilisant le nom de classe de l’objet suivi
d’un caractère de soulignement et de la chaîne “object”. Par exemple, pour
réaliser un lien vers l’objet TCustomPanel, utilisez le code suivant :
!AL(TCustomPanel_object,1)
Pour réaliser un lien vers une propriété, une méthode ou un événement, faites
précéder son nom par celui de l’objet qui l’implémente et par un caractère de
soulignement. Par exemple, pour réaliser un lien vers la propriété Text
implémentée par TControl, utilisez le code suivant :
!AL(TControl_Text,1)
Pour voir un exemple de rubriques de navigation secondaires, affichez l’aide
d’un composant quelconque et cliquez sur les liens étiquetés hiérarchie,
propriétés, méthodes ou événements.
3 Chaque propriété, événement ou méthode déclaré à l’intérieur du composant
doit avoir une rubrique.
Une rubrique décrivant une propriété, un événement ou une méthode doit
indiquer la déclaration de l’élément et décrire son rôle. Les développeurs
d’applications accéderont à cette rubrique en sélectionnant l’élément dans
l’inspecteur d’objets et en appuyant sur F1, ou en plaçant le curseur dans
l’éditeur de code sur le nom de l’élément et en appuyant sur F1. Pour avoir
un exemple de rubrique associée à une propriété, sélectionnez un élément
quelconque dans l’inspecteur d’objets et appuyez sur F1.
Les rubriques de propriété, d’événement et de méthode doivent inclure une
note de bas de page K qui indique le nom de la propriété, de l’événement et
de la méthode, son nom et celui du composant. Ainsi, la propriété Text de
TControl présente la note de bas de page K suivante :
Text,TControl;TControl,Text;Text,
Les rubriques de propriété, de méthode ou d’événement doivent également
comporter une note de bas de page $ indiquant le titre de la rubrique, tel que
TControl.Text.
Toutes ces rubriques doivent disposer d’un identificateur de rubrique unique à la
rubrique, entré sous la forme d’une note de bas de page #.

Aide contextuelle des composants


Chaque rubrique de composant, de propriété, de méthode et d’événement doit
avoir une note de bas de page A. La note de bas de page A permet d’afficher la
rubrique lorsque l’utilisateur sélectionne un composant et appuie sur F1, ou
lorsqu’il sélectionne une propriété ou un événement dans l’inspecteur d’objets et
appuie sur F1. Les notes de bas de page A doivent suivre certaines conventions
d’appellation :

38-6 Guide du développeur


Ajout d’éditeurs de propriétés

Si la rubrique d’aide est destinée à un composant , la note de bas de page A


comprend deux entrées séparées par un point-virgule selon la syntaxe suivante :
ComponentClass_Object;ComponentClass
où ComponentClass est le nom de la classe du composant.
Si la rubrique d’aide est destinée à une propriété ou à un événement, la note de
bas de page A comprend trois entrées séparées par des points-virgules selon la
syntaxe suivante :
ComponentClass_Element;Element_Type;Element
où ComponentClass est le nom de la classe du composant, Element est le nom de
la propriété, de la méthode ou de l’événement et Type est la propriété, la
méthode ou l’événement
Par exemple, en supposant une propriété BackgroundColor d’un composant
TMyGrid, la note de bas de page A est
TMyGrid_BackgroundColor;BackgroundColor_Property;BackgroundColor

Ajout des fichiers d’aide des composants


Pour ajouter votre fichier d’aide à Delphi, utilisez l’utilitaire OpenHelp (appelé
oh.exe) situé dans le répertoire bin ou en utilisant Aide|Personnaliser dans l’EDI.
Vous obtiendrez des informations sur le fichier OpenHelp.hlp sur l’utilisation de
OpenHelp, ainsi que sur l’ajout de votre fichier d’aide au système d’aide.

Ajout d’éditeurs de propriétés


L’inspecteur d’objets permet par défaut de modifier tous les types de propriétés.
Vous pouvez toutefois créer un éditeur pour des propriétés spécifiques en
l’écrivant et en le recensant. Vous pouvez recenser les éditeurs de propriétés afin
qu’ils s’appliquent uniquement aux propriétés des composants dont vous êtes
l’auteur, ou à toutes les propriétés du type spécifié.
A la base, un éditeur de propriétés peut opérer selon deux modes : affichage
sous la forme d’une chaîne texte permettant à l’utilisateur la modification de la
valeur courante ; affichage d’une boîte de dialogue permettant des modifications
d’une autre sorte. Selon la propriété en cours de modification, vous pourrez faire
appel à l’un ou l’autre mode.
L’écriture d’un éditeur de propriété se déroule en cinq étapes :
1 Dérivation d’une classe éditeur de propriétés
2 Modification de la propriété sous une forme textuelle
3 Modification globale de la propriété
4 Spécification des attributs de l’éditeur
5 Recensement de l’éditeur de propriétés

Accessibilité des composants au moment de la conception 38-7


Ajout d’éditeurs de propriétés

Dérivation d’une classe éditeur de propriétés


L’unité DsgnIntf définit plusieurs sortes d’éditeurs de propriétés, tous descendant
de TPropertyEditor. Lorsque vous créez un éditeur de propriétés, votre classe
éditeur de propriétés peut descendre directement de TPropertyEditor ou d’un des
types d’éditeurs de propriétés décrits dans le tableau suivant.
L’unité DsgnIntf définit également certains éditeurs spécialisés utilisés
exclusivement par certaines propriétés comme le composant Name. Les éditeurs
de propriétés ci-dessous sont les plus utiles aux concepteurs de propriétés
définies par l’utilisateur.
Tableau 38.1 Types d’éditeurs de propriétés prédéfinis
Type Propriétés modifiables
TOrdinalProperty Tous les éditeurs de valeurs ordinales (propriétés de type entier,
caractères, énuméré) sont des descendants de TOrdinalProperty.
TIntegerProperty Tous les types entiers y compris ceux prédéfinis ainsi que les
intervalles utilisateur.
TCharProperty Le type Char et les intervalles de valeurs Char tels que ‘A’..’Z’.
TEnumProperty Tous les types énumérés.
TFloatProperty Tous les nombres à virgule flottante.
TStringProperty Toutes les chaînes.
TSetElementProperty Les éléments des ensembles comme valeurs booléennes
TSetProperty Tous les ensembles. Les ensembles ne sont pas directement
modifiables mais peuvent être développés sous la forme d’une liste
de propriétés que sont les éléments de l’ensemble.
TClassProperty Classes. Affiche le nom de la classe et se développe pour afficher
les propriétés de la classe.
TMethodProperty Pointeurs sur des méthodes, le plus souvent des événements.
TComponentProperty Les composants de la même fiche. Ne permet pas la modification
des propriétés des composants, mais peut pointer sur un composant
spécifique de type compatible.
TColorProperty Les couleurs d’un composant. Montre si possible les constantes de
couleurs ou à défaut leurs valeurs en hexadécimal. Une liste
déroulante affiche les constantes de couleurs. Un double-clic a pour
effet d’ouvrir la boîte de dialogue de sélection des couleurs.
TFontNameProperty Les noms de fontes. La liste déroulante affiche toutes les fontes
actuellement installées.
TFontProperty Les fontes. Autorise le développement des propriétés d’une fonte
particulière et offre l’accès à la boîte de dialogue des fontes.

L’exemple suivant montre la déclaration d’un éditeur de propriétés simple


nommé TMyPropertyEditor :
type
TFloatProperty = class(TPropertyEditor)
public
function AllEqual: Boolean; override ;
function GetValue: string; override ;
procedure SetValue(const Value: string); override ;
end;

38-8 Guide du développeur


Ajout d’éditeurs de propriétés

Modification de la propriété sous une forme textuelle


Toutes les propriétés doivent fournir une représentation de type chaîne de leurs
valeurs en vue de leur affichage dans l’inspecteur d’objets. Dans le cas de la
plupart des propriétés, le développeur pourra saisir une nouvelle valeur lors de
la conception. Les classes éditeur de propriétés fournissent des méthodes
virtuelles que vous pouvez surcharger afin de convertir le texte de la propriété
en sa valeur réelle.
Les méthodes à surcharger sont GetValue et SetValue. Votre éditeur de propriétés
hérite également d’un ensemble de méthodes utilisées pour affecter et lire
différentes sortes de valeurs comme le montre le tableau suivant.

Tableau 38.2 Méthodes pour lire et écrire les valeurs des propriétés
Type de propriété Méthode Get Méthode Set
Virgule flottante GetFloatValue SetFloatValue
Pointeur de méthode (événement) GetMethodValue SetMethodValue
Type ordinal GetOrdValue SetOrdValue
Chaîne GetStrValue SetStrValue

Lorsque vous surchargez une méthode GetValue, appelez l’une des méthodes
“Get”. Lorsque vous surchargez SetValue, appelez l’une des méthodes “Set”.

Affichage de la valeur de la propriété


La méthode GetValue de l’éditeur de propriétés renvoie une chaîne représentant
la valeur en cours de la propriété. L’inspecteur d’objets utilise cette chaîne dans
la colonne des valeurs pour cette propriété. Par défaut, GetValue renvoie inconnu.
Pour fournir une représentation sous une forme chaîne, vous devez surcharger la
méthode GetValue de l’éditeur de propriétés.
Si la propriété n’est pas une valeur chaîne, votre méthode GetValue doit convertir
la valeur en une chaîne.

Définition de la valeur de la propriété


La méthode SetValue de l’éditeur de propriétés accepte la chaîne saisie dans
l’inspecteur d’objets, la convertit dans le type approprié, et définit la propriété. Si
la chaîne n’est pas une valeur convenant à la propriété, SetValue doit déclencher
et ignorer la valeur.
Pour lire la valeur d’une propriété, vous devez surcharger la méthode SetValue
de l’éditeur de propriétés.
SetValue doit convertir la chaîne et la valider avant d’appeler une des méthodes.

Accessibilité des composants au moment de la conception 38-9


Ajout d’éditeurs de propriétés

Voici les méthodes GetValue et SetValue de TIntegerProperty. Integer est de type


ordinal, ainsi GetValue appelle GetOrdValue et convertit le résultat en chaîne.
SetValue convertit la chaîne en entier, effectue certains calculs d’intervalle et
appelle SetOrdValue.
function TIntegerProperty.GetValue: string;
begin
Result := IntToStr(GetOrdValue);
end;
procedure TIntegerProperty.SetValue(const Value: string);
var
L: Longint;
begin
L := StrToInt(Value); { convertit la chaîne en nombre}
with GetTypeData(GetPropType)^ do { ceci utilise les données du compilateur pour
// le type Integer }
if (L < MinValue) or (L > MaxValue) then { vérifie qu’il est dans l’intervalle... }
raise EPropertyError.Create( { ...sinon, déclenche l’exception }
FmtLoadStr(SOutOfRange, [MinValue, MaxValue]));
SetOrdValue(L); { s’il l’est, continue et définit la valeur }
end;
Les spécificités des exemples particuliers sont moins importantes qu’en principe :
GetValue convertit la valeur en chaîne ; SetValue convertit la chaîne et valide la
valeur avant d’appeler une des méthodes “Set”.

Modification globale de la propriété


Si vous le souhaitez, vous pouvez fournir une boîte de dialogue pour la
définition de la propriété. L’utilisation la plus courante des éditeurs de propriétés
concerne les propriétés qui sont des classes. Un exemple est la propriété Font,
qui a une boîte de dialogue éditeur associée permettant au développeur de
choisir tous les attributs de fonte en même temps.
Pour fournir une boîte de dialogue de définition globale de la propriété,
surchargez la méthode Edit de la classe éditeur de propriétés.
Les méthodes Edit utilisent les mêmes méthodes “Get” et “Set” utilisées dans les
méthodes GetValue et SetValue ; une méthode Edit appelle à la fois une méthode
“Get” et une méthode “Set”. Comme l’éditeur est spécifique du type, il est
habituellement inutile de convertir les valeurs des propriétés en chaînes.
L’éditeur traite généralement la valeur telle qu’elle a été récupérée.
Lorsque l’utilisateur clique sur le bouton ‘...’ à côté de la propriété, ou double-
clique sur la colonne des valeurs, l’inspecteur d’objets appelle la méthode Edit de
l’éditeur de propriétés.
Pour votre implémentation de la méthode Edit, suivez ces étapes :
1 Construisez l’éditeur que vous utilisez pour cette propriété.
2 Lisez la valeur en cours et attribuez-la à la propriété en utilisant une méthode
“Get”.

38-10 Guide du développeur


Ajout d’éditeurs de propriétés

3 Lorsque l’utilisateur sélectionne une nouvelle valeur, attribuez cette valeur à la


propriété en utilisant une méthode “Set”.
4 Détruisez l’éditeur.
Les propriétés Color trouvées dans la plupart des composants utilisent la boîte de
dialogue de couleur standard Windows comme éditeur de propriétés. Voici la
méthode Edit issue de TColorProperty, qui appelle la boîte de dialogue et utilise le
résultat :
procedure TColorProperty.Edit;
var
ColorDialog: TColorDialog;
begin
ColorDialog := TColorDialog.Create(Application); { construit l’éditeur }
try
ColorDialog.Color := GetOrdValue; { utilise la valeur existante }
if ColorDialog.Execute then { si l’utilisateur valide la boîte de dialogue par OK... }
SetOrdValue(ColorDialog.Color); { ...utilise le résultat pour définir la valeur }
finally
ColorDialog.Free; { détruit l’éditeur }
end;
end;

Spécification des attributs de l’éditeur


L’éditeur de propriétés doit fournir les informations permettant à l’inspecteur
d’objets de déterminer les outils à afficher. Par exemple, l’inspecteur a besoin de
savoir si la propriété a des sous-propriétés, ou s’il doit afficher la liste des
valeurs possibles de la propriété.
Pour spécifier les attributs de l’éditeur, vous devez surcharger sa méthode
GetAttributes.
GetAttributes renvoie un ensemble de valeurs de type TPropertyAttributes qui peut
inclure une ou plusieurs des valeurs suivantes :

Tableau 38.3 Indicateurs des attributs des éditeurs de propriétés


Indicateur Méthode associée Signification si inclus
paValueList GetValues L’éditeur peut fournir une liste de valeurs énumérées.
paSubProperties GetProperties La propriété dispose de sous-propriétés qu’il est
possible d’afficher.
paDialog Edit L’éditeur peut afficher une boîte de dialogue
permettant de modifier globalement la propriété.
paMultiSelect N/A La propriété doit s’afficher lorsque l’utilisateur
sélectionne plusieurs composants.
paAutoUpdate SetValue Mise à jour du composant après chaque modification
au lieu d’attendre l’approbation de la valeur.

Accessibilité des composants au moment de la conception 38-11


Ajout d’éditeurs de propriétés

Tableau 38.3 Indicateurs des attributs des éditeurs de propriétés (suite)


Indicateur Méthode associée Signification si inclus
paSortList N/A L’inspecteur d’objets doit trier la liste de valeurs.
paReadOnly N/A La valeur de la propriété ne peut être modifiée lors
de la conception.
paRevertable N/A Active l’élément de menu Revenir à hérité dans le
menu contextuel de l’inspecteur d’objets. Cet élément
de menu demande à l’éditeur d’annuler la valeur en
cours de la propriété et de revenir à une valeur par
défaut ou standard préalablement établie.

Les propriétés Color sont plus polyvalentes que la plupart des autres propriétés,
l’utilisateur dispose de plusieurs moyens pour sélectionner une couleur dans
l’inspecteur d’objets : il peut taper une valeur, sélectionner dans une liste ou faire
appel à l’éditeur personnalisé. C’est pourquoi la méthode GetAttributes de
TColorProperty, inclut plusieurs attributs dans la valeur qu’elle renvoie :
function TColorProperty.GetAttributes: TPropertyAttributes;
begin
Result := [paMultiSelect, paDialog, paValueList];
end;

Recensement de l’éditeur de propriétés


Lorsque l’éditeur de propriétés est créé, vous devez le recenser dans Delphi. Le
recensement d’un éditeur de propriétés associe un type de propriété et un
éditeur spécifique. Vous pouvez recenser un éditeur pour toutes les propriétés
d’un type particulier ou juste pour une propriété particulière d’un type de
composant particulier.
Pour recenser un éditeur de propriétés, appelez une procédure
RegisterPropertyEditor.
RegisterPropertyEditor prend quatre paramètres :
• Un pointeur de type information décrivant le type de la propriété à modifier.
Il s’agit toujours d’un appel à la fonction intégrée TypeInfo, telle que
TypeInfo(TMyComponent).
• Le type du composant auquel s’applique cet éditeur. Si ce paramètre est nil,
l’éditeur s’applique à toutes les propriétés d’un type donné.
• Le nom de la propriété. Ce paramètre n’est significatif que si le paramètre qui
le précède spécifie un type particulier de composant. Dans ce cas, vous
pouvez spécifier une propriété de ce type auquel s’applique l’éditeur.
• Le type d’éditeur de propriétés à utiliser pour modifier la propriété spécifiée.

38-12 Guide du développeur


Ajout d’éditeurs de composants

Voici un extrait de la procédure qui recense les éditeurs des composants


standard inclus dans la palette :
procedure Register;
begin
RegisterPropertyEditor(TypeInfo(TComponent), nil, ‘‘, TComponentProperty);
RegisterPropertyEditor(TypeInfo(TComponentName), TComponent, ‘Name’,
TComponentNameProperty);
RegisterPropertyEditor(TypeInfo(TMenuItem), TMenu, ‘‘, TMenuItemProperty);
end;
Les trois instructions de cette procédure couvrent les différentes utilisations de
RegisterPropertyEditor:
• La première instruction est la plus typique. Elle recense l’éditeur de propriétés
TComponentProperty pour toutes les propriétés de type TComponent (ou les
descendants de TComponent qui n’ont pas d’éditeur spécifique recensé).
Habituellement, vous créez un éditeur s’appliquant à un type particulier, puis
vous souhaitez l’utiliser pour l’ensemble des propriétés de ce type. C’est
pourquoi le deuxième et le troisième paramètres ont pour valeurs respectives
nil et une chaîne vide.
• La deuxième instruction est le type de recensement le plus spécifique. Elle
recense un éditeur pour une propriété spécifique et pour un type spécifique
de composant. Dans notre exemple, l’éditeur s’applique à la propriété Name
(de type TComponentName) de tous les composants.
• La troisième instruction est plus spécifique que la première, et moins que la
deuxième. Elle recense un éditeur pour toutes les propriétés de type
TMenuItem pour les composants de type TMenu.

Ajout d’éditeurs de composants


Les éditeurs de composants déterminent ce qui se passe lorsque vous double-
cliquez sur le composant dans le concepteur et ajoutent des commandes au
menu contextuel qui apparaît lorsque vous cliquez sur le composant avec le
bouton droit. Ils peuvent également copier votre composant dans le Presse-
papiers Windows dans des formats personnalisés.
Si vous n’attribuez pas d’éditeur à vos composants, Delphi utilise l’éditeur de
composants par défaut. Ce dernier est implémenté par la classe TDefaultEditor.
TDefaultEditor n’ajoute aucun nouvel élément au menu contextuel d’un
composant. Lorsque vous double-cliquez sur le composant, TDefaultEditor
recherche ses propriétés et génère le premier gestionnaire d’événement trouvé ou
s’y rend.
Pour ajouter des éléments au menu contextuel, modifier le comportement du
composant lorsque vous double-cliquez dessus ou ajouter de nouveaux formats
de Presse-papiers, dérivez une nouvelle classe à partir de TComponentEditor et
recensez-la pour qu’elle soit utilisée avec votre composant. Dans vos méthodes
surchargées, vous pouvez utiliser la propriété Component de TComponentEditor
pour accéder au composant en cours de modification.

Accessibilité des composants au moment de la conception 38-13


Ajout d’éditeurs de composants

L’ajout d’un éditeur de composants personnalisé comprend plusieurs étapes :


• Ajout d’éléments au menu contextuel
• Modification du comportement suite à un double-clic
• Ajout de formats de Presse-papiers
• Recensement d’un éditeur de composants

Ajout d’éléments au menu contextuel


Lorsque l’utilisateur clique avec le bouton droit sur le composant, les méthodes
GetVerbCount et GetVerb de l’éditeur de composants sont appelées pour
construire un menu contextuel. Vous pouvez surcharger ces méthodes pour
ajouter des commandes (verbes) au menu contextuel.
L’ajout d’éléments au menu contextuel requiert ces étapes :
• Spécification d’éléments de menu
• Implémentation des commandes

Spécification d’éléments de menu


Surchargez la méthode GetVerbCount pour renvoyer le nombre de commandes
que vous ajoutez au menu contextuel. Surchargez la méthode GetVerb pour
renvoyer les chaînes qui doivent être ajoutées pour chacune de ces commandes.
Lorsque vous surchargez GetVerb, ajoutez un “et” commercial (&) dans une
chaîne afin que le caractère suivant apparaisse souligné dans le menu contextuel
et fasse office de touche de raccourci pour la sélection de l’élément du menu.
Veillez à ajouter des points de suspension (...) à la fin d’une commande si elle
fait apparaître une boîte de dialogue. GetVerb possède un paramètre unique pour
indiquer l’index de la commande.
Le code suivant surcharge les méthodes GetVerbCount et GetVerb pour ajouter
deux commandes au menu contextuel.
function TMyEditor.GetVerbCount: Integer;
begin
Result := 2;
end;
function TMyEditor.GetVerb(Index: Integer): String;
begin
case Index of
0: Result := “&DoThis ...”;
1: Result := “Do&That”;
end;
end;
Remarque Veillez à ce que votre méthode GetVerb renvoie une valeur pour chaque index
possible indiqué par GetVerbCount.

38-14 Guide du développeur


Ajout d’éditeurs de composants

Implémentation des commandes


Lorsque la commande fournie par GetVerb est sélectionnée dans le concepteur, la
méthode ExecuteVerb est appelée. Pour chaque commande que vous spécifiez
dans la méthode GetVerb, implémentez une action dans la méthode ExecuteVerb.
Vous pouvez accéder au composant en cours de modification à l’aide de la
propriété Component de l’éditeur.
Par exemple, la méthode ExecuteVerb suivante implémente les commandes de la
méthode GetVerb de l’exemple précédent .
procedure TMyEditor.ExecuteVerb(Index: Integer);
var
MySpecialDialog: TMyDialog;
begin
case Index of
0: begin
MyDialog := TMySpecialDialog.Create(Application); { instantie l’éditeur }
if MySpecialDialog.Execute then; { si l’utilisateur valide la
// boîte de dialogue par OK... }
MyComponent.FThisProperty := MySpecialDialog.ReturnValue; { ...utilise la valeur}
MySpecialDialog.Free; { détruit l’éditeur }
end;
1: That; { appelle la méthode That }
end;
end;

Modification du comportement suite à un double-clic


Lorsque vous double-cliquez sur le composant, la méthode Edit du composant
est appelée. Par défaut, la méthode Edit exécute la première commande ajoutée
au menu contextuel. Ainsi, dans l’exemple précédent, le fait de double-cliquer
sur le composant exécute la commande DoThis.
Même si l’exécution de la première commande est généralement une bonne idée,
vous pouvez modifier ce comportement par défaut. Par exemple, vous pouvez
définir un comportement différent si :
• vous n’ajoutez aucune commande au menu contextuel ;
• vous souhaitez afficher une boîte de dialogue qui combine plusieurs
commandes lorsque l’utilisateur double-clique sur le composant.
Surchargez la méthode Edit pour spécifier un nouveau comportement lorsque
l’utilisateur double-clique sur le composant. Par exemple, la méthode Edit
suivante appelle une boîte de dialogue de fontes lorsque l’utilisateur double-
clique sur le composant :
procedure TMyEditor.Edit;
var
FontDlg: TFontDialog;

Accessibilité des composants au moment de la conception 38-15


Ajout d’éditeurs de composants

begin
FontDlg := TFontDialog.Create(Application);
try
if FontDlg.Execute then
MyComponent.FFont.Assign(FontDlg.Font);
finally
FontDlg.Free
end;
end;
Remarque Si vous souhaitez qu’un double-clic sur le composant affiche l’éditeur de code
d’un gestionnaire d’événement, utilisez TDefaultEditor comme classe de base pour
votre éditeur de composants au lieu de TComponentEditor. Puis, au lieu de
surcharger la méthode Edit, surchargez la méthode protégée
TDefaultEditor.EditProperty. EditProperty recherche les gestionnaires d’événement
du composant et affiche le premier qu’il trouve. Vous pouvez modifier ce
comportement pour visualiser un événement particulier. Par exemple :
procedure TMyEditor.EditProperty(PropertyEditor: TPropertyEditor;
Continue, FreeEditor: Boolean)
begin
if (PropertyEditor.ClassName = ‘TMethodProperty’) and
(PropertyEditor.GetName = ‘OnSpecialEvent’) then
// DefaultEditor.EditProperty(PropertyEditor, Continue, FreeEditor);
end;

Ajout de formats de Presse-papiers


Par défaut, lorsque l’utilisateur choisit Copier lorsqu’un composant est sélectionné
dans l’EDI, le composant est copié dans le format interne de Delphi. Il peut ensuite
être collé dans une autre fiche ou module de données. Votre composant peut copier
d’autres formats dans le Presse-papiers en surchargeant la méthode Copy.
Par exemple, la méthode Copy suivante permet à un composant TImage de copier
son image dans le Presse-papiers. Cette image est ignorée par l’EDI de Delphi,
mais peut être collée dans d’autres applications.
procedure TMyComponent.Copy;
var
MyFormat : Word;
AData,APalette : THandle;
begin
TImage(Component).Picture.Bitmap.SaveToClipBoardFormat(MyFormat, AData, APalette);
ClipBoard.SetAsHandle(MyFormat, AData);
end;

Recensement d’un éditeur de composants


Une fois que l’éditeur de composants est défini, il peut être recensé pour être
utilisé avec une classe composant particulière. Un éditeur de composants recensé
est créé pour chaque composant de cette classe lorsqu’il est sélectionné dans le
concepteur de fiche.

38-16 Guide du développeur


Catégories de propriété

Pour associer un éditeur de composants à une classe composant, appelez


RegisterComponentEditor. RegisterComponentEditor adopte le nom de la classe
composant qui utilise l’éditeur et le nom de la classe éditeur de composants que
vous avez définie. Par exemple, l’instruction suivante recense une classe éditeur
de composants nommée TMyEditor en vue de son utilisation avec tous les
composants de type TMyComponent :
RegisterComponentEditor(TMyComponent, TMyEditor);
Placez l’appel à RegisterComponentEditor dans la procédure Register où vous
recensez votre composant. Par exemple, si un nouveau composant nommé
TMyComponent et son éditeur de composants TMyEditor sont tous les deux
implémentés dans la même unité, le code suivant recense le composant et son
association à l’éditeur de composants.
procedure Register;
begin
RegisterComponents('Miscellaneous', [TMyComponent);
RegisterComponentEditor(classes[0], TMyEditor);
end;

Catégories de propriété
Dans l’IDE Delphi, l’inspecteur d’objet fournit au programmeur la possibilité de
masquer et d’afficher sélectivement des propriétés basées sur les catégories de
propriété. Les propriétés des nouveaux composants personnalisés peuvent aussi
rentrer dans ce procédé en recensant des propriétés par catégories. Faites ceci
alors que le composant est en cours de recensement en appelant une des
fonctions de recensement de propriété RegisterPropertyInCategory ou
RegisterPropertiesInCategory. Utilisez la première fonction pour recenser une
seule propriété et la dernière pour recenser plusieurs propriétés dans un seul
appel de fonction. Ces fonctions sont définies dans l’unité DsgnIntf.
Notez qu’il n’est pas obligatoire de recenser des propriétés ni que toutes les
propriétés d’un composant personnalisé soient recensées lorsque quelques-unes le
sont. Toute propriété non explicitement associée à une catégorie est considérée
simplement comme étant dans la catégorie TMiscellaneousCategory. Ces
propriétés seront affichées ou masquées dans l’inspecteur d’objet selon cette
catégorisation par défaut.
Delphi propose treize catégories de propriété de stockage, sous la forme de
classes de propriété. Recensez une propriété d’un nouveau composant
personnalisé dans l’une de ces catégories ou créez vos propres classes de
catégorie de propriété à partir de ces classes intégrées.
En plus de ces deux fonctions de recensement de propriétés, il existe une
fonction IsPropertyInCategory. Cette fonction est utile pour des tentatives telles
que la création d’utilitaires de localisation, dans laquelle vous devez déterminer
si une propriété est recensée dans une catégorie de propriété donnée.

Accessibilité des composants au moment de la conception 38-17


Recensement d’une propriété à la fois
Vous pouvez recenser une propriété à la fois et l’associer à une catégorie de
propriété en utilisant la fonction RegisterPropertyInCategory.
RegisterPropertyInCategory est fournie dans quatre variations surchargées,
chacune proposant un ensemble différent de critères pour l’identification de la
propriété dans le composant personnalisé associé à la catégorie de propriété.
La première variation vous permet d’identifier la propriété selon son nom. La
ligne ci-après recense une propriété associée à l’affichage visuel du composant,
en identifiant la propriété par son nom, “AutoSize”.
RegisterPropertyInCategory(TVisualCategory, 'AutoSize');
La deuxième variation identifie la propriété en utilisant le type de classe de
composant et le nom de propriété caractéristiques. L’exemple ci-après recense
(dans la catégorie THelpCategory) une propriété appelée “HelpContext” d’un
composant de la classe personnalisée TMyButton.
RegisterPropertyInCategory(THelpCategory, TMyButton, 'HelpContext');
La troisième variation utilise le type de propriété et son nom pour identifier la
propriété. L’exemple ci-après recense une propriété à partir d’une combinaison
de son type, Integer, et de son nom, “Width”.
RegisterPropertyInCategory(TVisualCategory, TypeInfo(Integer), 'Width');
La dernière variation identifie la propriété en n’utilisant que son type. L’exemple
ci-après recense une propriété à partir de son type, Integer.
RegisterPropertyInCategory(TVisualCategory, TypeInfo(Integer));
Consultez la section Classes de catégorie de propriété pour une liste des
catégories de propriétés disponibles et une brève description de leur utilisation.

Recensement de plusieurs propriétés en une seule fois


Vous pouvez recenser plusieurs propriétés en une seule fois et les associer à une
catégorie de propriété en utilisant la fonction RegisterPropertiesInCategory.
RegisterPropertiesInCategory est fournie dans trois variations surchargées,
chacune proposant un ensemble différent de critères pour l’identification de la
propriété dans le composant personnalisé associé à la catégorie de propriété.
La première variation vous permet d’identifier des propriétés pour les associer à
une catégorie de propriété à partir du nom de propriété. Une liste de noms de
propriétés est transmise sous forme de tableau de chaînes (String). Chaque
propriété identifiée par un nom dans la liste est recensée sous la catégorie de
propriété indiquée. Dans l’exemple ci-après, quatre propriétés sont recensées
dans la catégorie THelpCategory. Ces quatre propriétés sont identifiées par leur
nom en utilisant les chaînes (String)“HelpContext”, “Hint”, “ParentShowHint” et
“ShowHint”.
RegisterPropertiesInCategory(THelpCategory, ['HelpContext', 'Hint', 'ParentShowHint',
'ShowHint']);

38-18 Guide du développeur


La deuxième variation identifie les propriétés selon leur type. Dans l’exemple ci-
après, toutes les propriétés du composant personnalisé de type String sont
recensées dans la catégorie TLocalizableCategory.
RegisterPropertiesInCategory(TLocalizableCategory, TypeInfo(String));
La troisième variation vous permet de transmettre une liste de plusieurs critères
(il n’est pas nécessaire qu’ils aient tous le même type) permettant d’identifier les
propriétés à recenser. La liste est transmise sous forme de tableau de constantes.
Dans l’exemple ci-après, toutes les propriétés ayant le nom “Text” ou appartenant
à une classe de type TEdit sont recensées dans la catégorie TLocalizableCategory.
RegisterPropertiesInCategory(TLocalizableCategory, ['Text', TEdit]);
Consultez la section Classes de catégorie de propriété pour obtenir une liste des
catégories de propriété disponibles ainsi qu’une brève description de leur
utilisation.

Classes de catégorie de propriété


Catégories de propriété intégrées
Delphi propose un ensemble intégré de douze catégories de propriété que vous
pouvez utiliser pour associer des propriétés dans des composants personnalisés.
Utilisez un de ces noms de classes de catégorie de propriété pour le paramètre
ACategoryClass des fonctions RegisterPropertyInCategory et
RegisterPropertiesInCategory.:

Tableau 38.4 Catégories de propriété


Catégorie Usage
TActionCategory Propriétés relatives aux actions d’exécution ; les propriétés Enabled et
Hint de TEdit se trouvent dans cette catégorie.
TDatabaseCategory Propriétés relatives aux opérations de base de données ; les propriétés
DatabaseName et SQL de TQuery se trouvent dans cette catégorie.
TDragNDropCategory Propriétés relatives aux opérations de glisser-déposer et d’empilement
; les propriétés DragCursor et DragKind de TImage se trouvent dans
cette catégorie.
THelpCategory Propriétés relatives à l’utilisation de l’aide en ligne ou des conseils ;
les propriétés HelpContext et Hint de TMemo se trouvent dans cette
catégorie.
TLayoutCategory Propriétés relatives à l’affichage visuel d’un contrôle à la conception ;
les propriétés Top et Left de TDBEdit se trouvent dans cette catégorie.
TLegacyCategory Propriétés relatives aux opérations obsolètes ; les propriétés Ctl3D et
ParentCtl3D de TComboBox se trouvent dans cette catégorie.
TLinkageCategory Propriétés relatives à l’association ou à la liaison d’un composant à
un autre ; la propriété DataSet de TDataSource se trouve dans cette
catégorie.
TLocaleCategory Propriétés relatives aux localisations internationales ; les propriétés
BiDiMode et ParentBiDiMode de TMainMenu se trouvent dans cette
catégorie.

Accessibilité des composants au moment de la conception 38-19


Tableau 38.4 Catégories de propriété (suite)
Catégorie Usage
TLocalizableCategory Propriétés relatives aux opérations de base de données ; les
propriétés DatabaseName et SQL de TQuery se trouvent dans cette
catégorie.
TMiscellaneousCategory Propriétés qui ne rentrent pas dans une catégorie ou n’ont pas besoin
d’être mises dans des catégories (et les propriétés non explicitement
recensées dans une catégorie spécifique) ; les propriétés AllowAllUp
et Name de TSpeedButton se trouvent dans cette catégorie.
TVisualCategory Propriétés relatives à l’affichage visuel d’un contrôle à l’exécution ;
les propriétés Align et Visible de TScrollBox se trouvent dans cette
catégorie.
TInputCategory Propriétés relatives à la saisie de données (il n’est pas nécessaire qu’elles
soient relatives aux opérations de base de données) ; les propriétés
Enabled et ReadOnly de TEdit se trouvent dans cette catégorie.

Dérivation de nouvelles catégories de propriétés


Vous pouvez créer de nouvelles catégories de propriétés par vous-même en
faisant dériver une classe de la classe de base TPropertyCategory ou d’un des
descendants intégrés. Consultez la section Classes de catégorie de propriété pour
obtenir la liste des catégories de propriétés disponibles ainsi qu’une brève
description de leur utilisation.
Lors d’une dérivation d’une nouvelle classe de catégorie de propriété,
redéfinissez la méthode Name. Cette méthode fournit le nom de la catégorie à
afficher dans l’inspecteur d’objet. La méthode Name peut simplement renvoyer
une valeur String ou elle peut récupérer une valeur d’une ressource. Cette
dernière possibilité est utile dans le cas d’une internationalisation simple d’un
composant personnalisé et de ses catégories.
Soit la nouvelle méthode Name ci-après pour une classe de catégorie
personnalisée, le texte “My Special” doit apparaître dans l’inspecteur d’objet
lorsque les catégories de propriétés sont affichées (et au moins l’une des
propriétés de l’objet en cours est recensée dans cette classe de propriété).
class function TMySpecialCategory.Name: String;
begin
Result := 'My Special';
end;

Utilisation de la fonction IsPropertyInCategory


Une application peut rechercher les propriétés recensées existantes afin de
déterminer si une propriété donnée est toujours recensée dans une catégorie
indiquée. Ceci peut être particulièrement utile dans des situations telles qu’un
utilitaire de localisation qui vérifie la catégorisation des propriétés afin de
préparer ses opérations de localisation. Deux variations surchargées de la
fonction IsPropertyInCategory sont disponibles, autorisant différents critères afin
de déterminer si une propriété se trouve dans une catégorie.

38-20 Guide du développeur


La première variation vous permet de baser le critère de comparaison sur une
combinaison du type de classe du composant propriétaire et du nom de la
propriété. Dans la ligne de commande ci-après, pour que IsPropertyInCategory
renvoie True, la propriété doit appartenir à la classe TEdit, avoir le nom “Text”,
et se trouver dans la catégorie de propriété TLocalizableCategory.
IsItThere := IsPropertyInCategory(TLocalizableCategory, TEdit, 'Text');
La deuxième variation vous permet de baser le critère de comparaison sur une
combinaison du nom de classe du composant propriétaire et du nom de la
propriété. Dans la ligne de commande ci-après, pour que IsPropertyInCategory
renvoie True, la propriété doit appartenir à la classe TEdit, avoir le nom“Text”,
et se trouver dans la catégorie de propriété TLocalizableCategory.
IsItThere := IsPropertyInCategory(TLocalizableCategory, 'TEdit', 'Text');

Compilation des composants en paquets


Une fois vos composants recensés, vous devez les compiler en paquets avant de
les installer dans l’EDI. Un paquet peut contenir un ou plusieurs composants
ainsi que des éditeurs de propriétés personnalisés. Pour plus d’informations sur
les paquets, voir chapitre 9, “Utilisation des paquets et des composants”.
Pour créer et compiler un paquet, voir “Création et modification de paquets” à
la page 9-8. Placez les unités de code source de vos composants personnalisés
dans la liste Contient du paquet. Si vos composants dépendent d’autres paquets,
incluez ces derniers dans la liste Nécessite.
Pour installer vos composants dans l’EDI, voir “Installation de paquets de
composants” à la page 9-6.

Problèmes d’installation de composants personnalisés


Un problème courant lors du recensement et de l’installation de composants
personnalisés est l’absence du composant dans la liste des composants après
l’installation correcte du paquet.
Voici les causes les plus courantes de l’absence du composant dans la liste des
composants ou sur la palette :
• Modificateur PACKAGE sur la fonction Register manquant
• Modificateur PACKAGE sur la classe manquant
• Absence de #pragma package(smart_init) dans le fichier source C++
• Fonction Register non trouvée dans un domaine d’appellation de même nom
que le module code source.

Accessibilité des composants au moment de la conception 38-21


• Register n’est pas correctement exportée. Utilisez tdump sur le fichier .BPL
pour rechercher la fonction exportée :
tdump -ebpl mypack.bpl mypack.dmp
Dans la section exports du dump, vous devriez voir la fonction Register (dans
le domaine d’appellation) exportée.

38-22 Guide du développeur


Chapitre

Modification d’un composant


Chapter 39
39
existant
Le moyen le plus simple de créer un composant consiste à le dériver d’un
composant qui réalise la presque totalité des fonctions souhaitées et lui apporter
ensuite quelques modifications. L’exemple de ce chapitre modifie le composant
mémo standard pour créer un mémo qui ne fait pas de saut de ligne par défaut.
La valeur de la propriété WordWrap du composant mémo est initialisée à True. Si
vous utilisez fréquemment des mémos n’effectuant pas de saut de ligne, vous
pouvez créer un nouveau composant mémo qui ne fait pas de saut de ligne par
défaut.
Remarque Pour modifier les propriétés publiées ou enregistrer des gestionnaires
d’événements spécifiques pour un composant existant, il est souvent plus simple
d’utiliser un modèle de composant plutôt que de créer une classe.
La modification d’un composant existant se fait en deux étapes :
• Création et recensement du composant
• Modification de la classe composant

Création et recensement du composant


La création d’un composant débute toujours de la même façon : vous créez une
unité, puis dérivez et recensez une classe composant avant de l’installer dans la
palette des composants. Ce processus est décrit dans “Création d’un nouveau
composant” à la page 31-8.
Pour notre exemple, suivez la procédure générale de création d’un composant en
tenant compte des spécificités suivantes :
• Nommez l’unité du composant Memos.

Modification d’un composant existant 39-1


Modification de la classe composant

• Dérivez un nouveau type de composant appelé TWrapMemo, descendant de


TMemo.
• Recensez TWrapMemo sur la page Exemples de la palette des composants.
Le fichier en-tête que vous obtenez doit ressembler à ceci :
unit Memos;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, StdCtrls;
type
TWrapMemo = class(TMemo)
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TWrapMemo]);
end;
end.
Si vous compilez et installez maintenant le nouveau composant, il se comportera
exactement comme son ancêtre, TMemo. Dans la section suivante, vous
effectuerez une simple modification à votre composant.

Modification de la classe composant


Une fois la nouvelle classe composant créée, vous pouvez lui apporter presque
toutes les modifications que vous voulez. Dans notre exemple, vous allez changer la
valeur par défaut d’une propriété du composant mémo. Cela implique deux
changements mineurs dans la classe composant :
• Surcharge du constructeur.
• Spécification de la nouvelle valeur par défaut de la propriété.
Le constructeur définit la valeur de la propriété. La valeur par défaut indique à
Delphi quelles valeurs stocker dans le fichier fiche (.DFM). Delphi ne stocke que
les valeurs qui diffèrent de la valeur par défaut, c’est pourquoi il est important
d’effectuer les deux étapes.

Surcharge du constructeur
Lorsque vous placez un composant dans une fiche au moment de la conception ou
lorsqu’une application en cours d’exécution construit un composant, le constructeur
du composant définit les valeurs des propriétés. Quand un composant est chargé
depuis un fichier fiche, l’application définit toutes les propriétés qui ont été
modifiées lors de la conception.

39-2 Guide du développeur


Modification de la classe composant

Remarque Lorsque vous surchargez un constructeur, le nouveau constructeur doit appeler le


constructeur reçu en héritage avant toute autre action. Pour plus d’informations,
voir “Surcharge des méthodes” à la page 32-9.
Dans notre exemple, votre nouveau composant doit surcharger le constructeur
transmis en héritage par TMemo en attribuant la valeur False à la propriété
WordWrap. Pour ce faire, ajoutez le constructeur surchargé à la prédéclaration,
puis écrivez le nouveau constructeur dans la partie implémentation de l’unité :
type
TWrapMemo = class(TMemo)
public { les constructeurs sont toujours publics }
constructor Create(AOwner: TComponent); override ; { cette syntaxe est toujours
// identique }
end;
ƒ
constructor TWrapMemo.Create(AOwner: TComponent); { ceci va après l’implémentation }
begin
inherited Create(AOwner); { Faites TOUJOURS ceci en premier ! }
WordWrap := False; { définit la nouvelle valeur désirée}
end;
Vous pouvez maintenant installer le nouveau composant sur la palette des
composants et l’ajouter à une fiche. Remarquez que la propriété WordWrap est
dorénavant initialisée à False.
Si vous changez une valeur de propriété initiale, vous devez aussi désigner cette
valeur comme étant celle par défaut. Si vous échouez à faire correspondre la
valeur définie par le constructeur à celle spécifiée par défaut, Delphi ne peut pas
stocker, ni restaurer la valeur correcte.

Spécification de la nouvelle valeur par défaut de la propriété


Lorsque Delphi stocke la description d’une fiche dans un fichier fiche, il ne stocke
que les valeurs des propriétés différentes des valeurs par défaut. La taille du fichier
fiche reste minime et le chargement est plus rapide. Si vous créez une propriété
ou si vous changez la valeur par défaut d’une propriété existante, c’est une bonne
idée de mettre à jour la déclaration de la propriété en y incluant la nouvelle valeur
par défaut. Les fichiers fiche ainsi que le chargement et les valeurs par défaut sont
expliqués en détail chapitre 38, “Accessibilité des composants au moment de la
conception”.
Pour changer la valeur par défaut d’une propriété, redéclarez le nom de la
propriété, suivi de la directive default et de la nouvelle valeur par défaut. Il
n’est pas nécessaire de redéclarer la propriété entière mais uniquement le nom et
la valeur par défaut.

Modification d’un composant existant 39-3


Modification de la classe composant

Pour le composant mémo de saut à la ligne automatique, redéclarez la propriété


WordWrap dans la partie published de la déclaration d’objet, avec la valeur False
par défaut :
type
TWrapMemo = class(TMemo)
ƒ
published
property WordWrap default False;
end;
Spécifier la valeur par défaut de la propriété n’affecte en rien le fonctionnement du
composant. Vous devez toujours initialiser la valeur dans le constructeur du
composant. La redéclaration de la valeur par défaut assure que Delphi connaît
quand WordWrap doit être écrit dans le fichier fiche.

39-4 Guide du développeur


Chapitre

Création d’un composant graphique


Chapter 40
40
Un contrôle graphique est un composant simple. Un contrôle purement graphique
ne reçoit jamais la focalisation et n’a donc pas besoin de handle de fenêtre. Les
utilisateurs peuvent quand même manipuler le contrôle avec la souris, mais il
n’y a pas d’interface clavier.
TShape, le composant graphique présenté dans ce chapitre, correspond au
composant forme inclus dans la page Supplément de la palette des composants.
Bien que le composant que nous allons créer soit identique au composant forme
standard, vous devrez lui donner un nom différent pour éviter des doublons
d’identificateur. Ce chapitre présente le composant forme TSampleShape et illustre
toutes les étapes de sa création :
• Création et recensement du composant
• Publication des propriétés héritées
• Ajout de fonctionnalités graphiques

Création et recensement du composant


La création d’un composant débute toujours de la même façon. Vous créez une
unité et vous recensez le composant avant de l’installer dans la palette des
composants. Ce processus est décrit dans “Création d’un nouveau composant” à
la page 31-8.
Pour cet exemple, suivez la procédure générale de création d’un composant en
tenant compte de ces spécifités :
• Appelez l’unité du composant Shapes.
• Dérivez un nouveau type de composant appelé TSampleShape, descendant de
TGraphicControl.
• Recensez TSampleShape sur la page Exemples de la palette des composants.

Création d’un composant graphique 40-1


Publication des propriétés héritées

L’unité que vous obtenez doit ressembler à ceci :


unit Shapes;
interface
uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms;
type
TSampleShape = class(TGraphicControl)
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponent('Samples', [TSampleShape]);
end;
end.

Publication des propriétés héritées


Lorsque vous dérivez un type de composant, vous choisissez parmi les propriétés
et les événements déclarés dans la partie protected de la classe ancêtre ceux que
vous voulez rendre disponibles aux utilisateurs du nouveau composant.
TGraphicControl publie toutes les propriétés qui permettent au composant de
fonctionner en tant que contrôle, donc les seules fonctionnalités que vous devez
publier sont celles dont vous avez besoin pour répondre aux événements de souris
et aux opérations glisser-déplacer.
La publication des propriétés et des événements reçus en héritage est expliquée
dans “Publication des propriétés héritées” à la page 33-3 et “Rendre visibles des
événements” à la page 34-6. Ces deux processus impliquent la redéclaration du
nom des propriétés dans la partie published de la déclaration de classe.
S’agissant de notre contrôle forme, vous devez publier les trois événements associés
à la souris ainsi que les deux propriétés associées aux opérations glisser-déplacer :
type
TSampleShape = class(TGraphicControl)
published
property DragCursor; { propriétés glisser-déplacer }
property DragMode;
property OnDragDrop; { événements glisser-déplacer }
property OnDragOver;
property OnEndDrag;
property OnMouseDown; { événements de souris }
property OnMouseMove;
property OnMouseUp;
end;
Le contrôle forme de notre exemple rend les interactions associées à la souris et
aux opérations glisser-déplacer accessibles à l’utilisateur.

40-2 Guide du développeur


Ajout de fonctionnalités graphiques

Ajout de fonctionnalités graphiques


Lorsque votre composant graphique a été déclaré et qu’ont été publiées toutes les
propriétés reçues en héritage que vous voulez rendre disponibles, vous pouvez
ajouter les fonctionnalités graphiques qui caractériseront votre composant :
1 Détermination de ce qui doit être dessiné.
2 Dessin de l’image du composant.
En outre, s’agissant du contrôle forme de notre exemple, vous allez ajouter
certaines propriétés qui serviront aux développeurs d’applications pour
personnaliser l’apparence du contrôle lors de la conception.

Détermination de ce qui doit être dessiné


Un contrôle graphique est capable de changer son apparence pour refléter un
changement de condition, y compris une intervention de l’utilisateur. Un contrôle
graphique qui aurait toujours le même aspect ne devrait pas être un composant. Si
vous voulez une image statique, importez une image au lieu d’utiliser un contrôle.
Généralement, l’apparence d’un contrôle graphique dépend de plusieurs
propriétés. Par exemple, le contrôle jauge dispose de propriétés qui déterminent
sa forme et son orientation et si la représentation de la progression est
numérique ou graphique. De même, notre contrôle forme doit disposer d’une
propriété qui détermine le type de forme qu’il doit dessiner.
Pour donner à votre contrôle une propriété qui détermine la forme dessinée,
ajoutez une propriété intitulée Shape. Ce processus se déroule en trois étapes :
1 Déclaration du type de la propriété.
2 Déclaration de la propriété.
3 Ecriture de la méthode d’implémentation.
La création de propriété est expliquée en détail chapitre 33, “Création de propriétés”

Déclaration du type de la propriété


Lorsque vous déclarez une propriété dont le type est défini par l’utilisateur, le type
de la propriété doit être déclaré avant la classe qui inclut cette propriété. Les types
énumérés sont fréquemment employés par les propriétés.
S’agissant de notre contrôle forme, vous aurez besoin d’un type énuméré avec
un élément défini pour chaque forme que le contrôle est en mesure de dessiner.
Ajoutez la définition de type suivante avant la déclaration de classe du contrôle
forme.
type
TSampleShapeType = (sstRectangle, sstSquare, sstRoundRect, sstRoundSquare,
sstEllipse, sstCircle);
TSampleShape = class(TGraphicControl) { existe déjà }
Vous pouvez maintenant utiliser ce type pour déclarer une nouvelle propriété dans
la classe.

Création d’un composant graphique 40-3


Ajout de fonctionnalités graphiques

Déclaration de la propriété
Généralement, pour déclarer une propriété, vous déclarez un champ privé pour
stocker les données de la propriété puis vous spécifiez les méthodes pour lire et/
ou écrire sa valeur. Souvent, la méthode pour lire la valeur n’est pas nécessaire
car un simple pointage sur la valeur stockée suffit.
S’agissant de notre contrôle forme, vous aurez à déclarer un champ contenant la
forme courante, puis à déclarer une propriété qui lit ce champ et l’écrit via un
appel de méthode.
Ajoutez les déclarations suivantes dans TSampleShape:
type
TSampleShape = class(TGraphicControl)
private
FShape: TSampleShapeType; { donnée membre pour contenir la valeur de la propriété }
procedure SetShape(Value: TSampleShapeType);
published
property Shape: TSampleShapeType read FShape write SetShape;
end;
Il ne vous reste plus qu’à ajouter l’implémentation de SetShape.

Ecriture de la méthode d’implémentation


Lorsque la partie read ou write d’une définition de propriété utilise une méthode
plutôt qu’un accès direct aux données stockées de la propriété, vous devez
implémenter ces méthodes.
Ajoutez l’implémentation de la méthode SetShape à la partie implémentation de
l’unité :
procedure TSampleShape.SetShape(Value: TSampleShapeType);
begin
if FShape <> Value then { ignore s’il n’y a pas eu de changement }
begin
FShape := Value; { stocke la nouvelle valeur }
Invalidate; { force le dessin avec la nouvelle forme }
end;
end;

Surcharge du constructeur et du destructeur


Pour changer les valeurs par défaut des propriétés et initialiser les classes
appartenant à votre composant, vous devez surcharger le constructeur et le
destructeur reçus en héritage. Dans les deux cas, n’oubliez pas d’appeler la méthode
reçue en héritage.

Modification des valeurs par défaut des propriétés


La taille par défaut d’un contrôle graphique étant réduite, vous pouvez modifier sa
largeur et sa hauteur dans le constructeur. La modification des valeurs par défaut
des propriétés est abordée plus en détail chapitre 39, “Modification d’un
composant existant”.

40-4 Guide du développeur


Ajout de fonctionnalités graphiques

Dans notre exemple, le contrôle forme définit sa taille par un carré de 65 pixels
de côté.
Ajoutez le constructeur surchargé dans la déclaration de la classe composant :
type
TSampleShape = class(TGraphicControl)
public { constructeurs toujours publics }
constructor Create(AOwner: TComponent); override { surcharger la directive }
end;
1 Redéclarez les propriétés Height et Width avec leurs nouvelles valeurs par
défaut :
type
TSampleShape = class(TGraphicControl)
ƒ
published
property Height default 65;
property Width default 65;
end;
2 Ecrivez le nouveau constructeur dans la partie implémentation de l’unité :
constructor TSampleShape.Create(AOwner: TComponent);
begin
inherited Create(AOwner); { appelle toujours le constructeur hérité }
Width := 65;
Height := 65;
end;

Publication du crayon et du pinceau


Par défaut, un canevas dispose d’un crayon fin et noir et d’un pinceau plein et
blanc. Pour permettre aux développeurs de changer le crayon et le pinceau, vous
devez leur fournir des classes pour les manipuler lors de la conception, puis
copier les classes dans le canevas lors des opérations de dessin. Des classes telles
qu’un crayon ou un pinceau auxiliaire sont appelées classes ayant un propriétaire car
elles appartiennent au composant qui est responsable de leur création et de leur
destruction.
La gestion des classes ayant un propriétaire se déroule en quatre étapes :
1 Déclaration des .
2 Déclaration des propriétés d’accès.
3 Initialisation des classes ayant un propriétaire.
4 Définition des propriétés des classes ayant un propriétaire.

Déclaration des champs de classe


Chaque classe appartenant à un composant doit avoir un champ déclaré dans ce
composant. Le champ de classe garantit que le composant dispose toujours d’un
pointeur sur l’objet qui lui appartient afin de lui permettre de le détruire avant de
se détruire lui-même. Généralement, un composant initialise les objets dont il est
propriétaire dans son constructeur et les détruit dans son destructeur.

Création d’un composant graphique 40-5


Ajout de fonctionnalités graphiques

Les champs de classe des objets ayant un propriétaire sont presque toujours
déclarés private. Si des applications (ou d’autres composants) ont besoin
d’accéder aux objets ayant un propriétaire, vous devez pour cela déclarer des
propriétés publiées ou publiques.
Ajoutez des champs de classe pour le crayon et le pinceau de votre contrôle forme :
type
TSampleShape = class(TGraphicControl)
private { les données membres sont presque toujours privées }
FPen: TPen; { donnée membre pour l’objet crayon }
FBrush: TBrush; { donnée membre pour l’objet pinceau }
ƒ
end;

Déclaration des propriétés d’accès


Vous pouvez fournir les accès aux objets appartenant à un composant en déclarant
des propriétés de même type que ces objets. Cela offre aux développeurs un moyen
d’accéder aux objets lors de la conception ou de l’exécution. Généralement, la
partie read de la propriété ne fait que référencer le champ de classe, alors que la
partie write appelle une méthode qui permet au composant de réagir aux
changements apportés à l’objet.
Ajoutez des propriétés à votre contrôle forme pour fournir les accès aux champs
du crayon et du pinceau. Vous allez également déclarer les méthodes pour réagir
aux changements de crayon ou de pinceau.
type
TSampleShape = class(TGraphicControl)
ƒ
private { ces méthodes doivent être privées }
procedure SetBrush(Value: TBrush);
procedure SetPen(Value: TPen);
published { les rend disponible lors de la conception }
property Brush: TBrush read FBrush write SetBrush;
property Pen: TPen read FPen write SetPen;
end;
Vous devez ensuite écrire les méthodes SetBrush et SetPen dans la partie
implémentation de l’unité :
procedure TSampleShape.SetBrush(Value: TBrush);
begin
FBrush.Assign(Value); { remplace le pinceau existant par le paramètre }
end;
procedure TSampleShape.SetPen(Value: TPen);
begin
FPen.Assign(Value); { remplace le crayon existant par le paramètre }
end;
Affecter directement le contenu de Value à FBrush...
FBrush := Value;
...écraserait le pointeur interne de FBrush, ferait perdre de la mémoire et créerait
une série de problèmes de propriété.

40-6 Guide du développeur


Ajout de fonctionnalités graphiques

Initialisation des classes ayant un propriétaire


Si vous ajoutez des classes dans votre composant, le constructeur de ce dernier doit
les initialiser pour que l’utilisateur puisse interagir avec ces objets lors de
l’exécution. De même, le destructeur du composant doit également détruire les
objets appartenant au composant avant de détruire ce dernier.
Comme vous avez déclaré un crayon et un pinceau dans le contrôle forme, vous
devez les initialiser dans le constructeur du contrôle forme et les détruire dans
son destructeur :
1 Construisez le crayon et le pinceau dans le constructeur du contrôle forme :
constructor TSampleShape.Create(AOwner: TComponent);
begin
inherited Create(AOwner); { appelle toujours le contrôle hérité }
Width := 65;
Height := 65;
FPen := TPen.Create; { construit le crayon }
FBrush := TBrush.Create; { construit le pinceau }
end;
2 Ajoutez le destructeur surchargé dans la déclaration de la classe composant :
type
TSampleShape = class(TGraphicControl)
public { les destructeurs sont toujours publics}
constructor Create(AOwner: TComponent); override ;
destructor Destroy; override ; { ne pas oublier la surcharge de directive }
end;
3 Ecrivez le nouveau destructeur dans la partie implémentation de l’unité :
destructor TSampleShape.Destroy;
begin
FPen.Free; { détruit l’objet crayon }
FBrush.Free; { détruit l’objet pinceau }
inherited Destroy; { appelle aussi toujours le destructeur hérité }
end;

Définition des propriétés des classes ayant un propriétaire


L’une des dernières étapes de la gestion des classes crayon et pinceau consiste à
provoquer un nouveau dessin du contrôle forme si le crayon ou le pinceau est
modifié. Comme les classes crayon et pinceau disposent toutes les deux d’un
événement OnChange, vous pouvez créer une méthode dans le contrôle forme et
faire pointer les deux événements OnChange sur cette méthode.
Ajoutez la méthode suivante au contrôle forme et effectuez la mise à jour du
constructeur du composant pour affecter aux événements du crayon et du
pinceau cette nouvelle méthode :
type
TSampleShape = class(TGraphicControl)
published
procedure StyleChanged(Sender: TObject);
end;
ƒ

Création d’un composant graphique 40-7


Ajout de fonctionnalités graphiques

implementation
ƒ
constructor TSampleShape.Create(AOwner: TComponent);
begin
inherited Create(AOwner); { appelez toujours le constructeur hérité }
Width := 65;
Height := 65;
FPen := TPen.Create; { construit le crayon }
FPen.OnChange := StyleChanged; { affecte la méthode à l’événement OnChange }
FBrush := TBrush.Create; { construit le pinceau }
FBrush.OnChange := StyleChanged; { affecte la méthode à l’événement OnChange }
end;
procedure TSampleShape.StyleChanged(Sender: TObject);
begin
Invalidate(True); { efface et redessine le composant }
end;
Ces modifications faites, le composant se redessine pour refléter tout changement
du crayon ou du pinceau.

Dessin de l’image du composant


L’essentiel d’un contrôle graphique se résume à sa façon de dessiner son image à
l’écran. Le type abstrait TGraphicControl définit une méthode appelée Paint que vous
devez surcharger pour peindre l’image voulue dans votre contrôle.
La méthode Paint de votre contrôle forme doit accomplir plusieurs tâches :
• Utiliser le crayon et le pinceau sélectionnés par l’utilisateur.
• Utiliser la forme sélectionnée.
• Ajuster les coordonnées pour que les carrés et les cercles utilisent une largeur
et une hauteur identiques.
La surcharge de la méthode Paint nécessite deux étapes :
1 Ajout de Paint dans la déclaration du composant.
2 Insertion de la méthode Paint dans la partie implémentation de l’unité.
S’agissant de notre contrôle forme, vous devez ajouter la déclaration suivante à
la déclaration de classe :
type
TSampleShape = class(TGraphicControl)
ƒ
protected
procedure Paint; override ;
ƒ
end;

40-8 Guide du développeur


Ajout de fonctionnalités graphiques

Vous devez ensuite écrire la méthode dans la partie implémentation de l’unité :


procedure TSampleShape.Paint;
begin
with Canvas do
begin
Pen := FPen; { copie le crayon du composant }
Brush := FBrush; { copie le pinceau du composant }
case FShape of
sstRectangle, sstSquare:
Rectangle(0, 0, Width, Height); { dessine les rectangles et carrés }
sstRoundRect, sstRoundSquare:
RoundRect(0, 0, Width, Height, Width div 4, Height div 4); { dessine des formes
// arrondies }
sstCircle, sstEllipse:
Ellipse(0, 0, Width, Height); { dessine des formes arrondies }
end;
end;
end;
Paint est appelée à chaque fois que le contrôle doit mettre à jour son image.
Windows indique aux contrôles de se dessiner eux-mêmes lorsqu’ils s’affichent pour
la première fois ou lorsqu’une fenêtre qui se trouvait au-dessus disparaît. En outre,
vous pouvez forcer le dessin en appelant Invalidate, comme le fait la méthode
StyleChanged.

Adaptation du dessin de la forme


Le contrôle forme standard effectue une tâche supplémentaire que ne fait pas
encore le contrôle forme de notre exemple : il gère les carrés et les cercles ainsi que
les rectangles et les ellipses. Pour ce faire, vous devez écrire le code qui trouve le
côté le plus petit et centre l’image.
Voici une méthode Paint parfaitement adaptée aux carrés et aux ellipses :
procedure TSampleShape.Paint;
var
X, Y, W, H, S: Integer;
begin
with Canvas do
begin
Pen := FPen; { copie le crayon du composant }
Brush := FBrush; { copie le pinceau du composant }
W := Width; { utilise la largeur du composant }
H := Height; { utilise la hauteur du composant }
if W < H then S := W else S := H; { enregistre du plus petit pour les cercles/carrés }
case FShape of { ajuste la hauteur, la largeur et la position }
sstRectangle, sstRoundRect, sstEllipse:
begin
X := 0; { l’origine est l’angle supérieur gauche de ces formes }
Y := 0;
end;

Création d’un composant graphique 40-9


Ajout de fonctionnalités graphiques

sstSquare, sstRoundSquare, sstCircle:


begin
X := (W - S) div 2; { centre horizontalement... }
Y := (H - S) div 2; { ...puis verticalement }
W := S; { utilise la dimension la plus petite pour la largeur... }
H := S; { ...et pour la hauteur }
end;
end;
case FShape of
sstRectangle, sstSquare:
Rectangle(X, Y, X + W, Y + H); { dessine les rectangles et les carrés }
sstRoundRect, sstRoundSquare:
RoundRect(X, Y, X + W, Y + H, S div 4, S div 4); { dessine des formes arrondies }
sstCircle, sstEllipse:
Ellipse(X, Y, X + W, Y + H); { dessine les formes arrondies }
end;
end;
end;

40-10 Guide du développeur


Chapitre

Personnalisation d’une grille


Chapter 41
41
Delphi fournit plusieurs composants abstraits que vous pouvez utiliser comme
points de départ pour personnaliser vos composants. Les grilles et les boîtes liste
sont les plus importants. Dans ce chapitre, nous allons voir comment créer un
petit calendrier en partant du composant grille de base TCustomGrid.
La création du calendrier nécessite les étapes suivantes :
• Création et recensement du composant
• Publication des propriétés héritées
• Modification des valeurs initiales
• Redimensionnement des cellules
• Remplissage des cellules
• Navigation de mois en mois et d’année en année
• Navigation de jour en jour
Le composant calendrier résultant est pratiquement identique au composant
TCalendar contenu dans la page Exemples de la palette des composants.

Création et recensement du composant


La création d’un composant débute toujours de la même façon. Vous créez une unité
et vous recensez le composant avant de l’installer dans la palette des composants. Ce
processus est décrit dans “Création d’un nouveau composant” à la page 31-8.
Dans cet exemple, suivez la procédure générale de création d’un composant,
avec les spécificités suivantes :
• Appelez l’unité du composant CalSamp.
• Dérivez un nouveau type de composant appelé TSampleCalendar, descendant
de TCustomGrid.
• Recensez TSampleCalendar dans la page Exemples de la palette des
composants.

Personnalisation d’une grille 41-1


Publication des propriétés héritées

Le fichier en-tête que vous obtenez doit ressembler à ceci :


unit CalSamp;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Grids;
type
TSampleCalendar = class(TCustomGrid)
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TSampleCalendar]);
end;
end.
Si vous installez le composant calendrier maintenant, vous verrez qu’il apparaît
sur la page Exemples. Les seules propriétés disponibles sont les propriétés de
contrôle les plus basiques. L’étape suivante consiste à rendre disponible certaines
des propriétés plus spécialisées aux utilisateurs du calendrier.
Remarque Bien que vous puissiez installer le composant calendrier exemple que vous venez
de compiler, n’essayez pas de le placer tout de suite sur une fiche. Le composant
TCustomGrid contient une méthode DrawCell abstraite qui doit être redéclarée
avant que les objets d’instance puissent être créés. La surcharge de la méthode
DrawCell est décrite dans “Remplissage des cellules”.

Publication des propriétés héritées


Le composant grille abstrait, TCustomGrid, fournit de nombreuses propriétés
protected. Vous pouvez choisir parmi ces propriétés celles que vous voulez rendre
accessibles aux utilisateurs du contrôle calendrier.
Pour rendre accessibles aux utilisateurs de vos composants les propriétés protégées
qu’ils reçoivent en héritage, vous devez redéclarer ces propriétés dans la partie
published de la déclaration de vos composants.
S’agissant du contrôle calendrier, vous devez publier les propriétés et les
événements, comme ci-dessous :
type
TSampleCalendar = class(TCustomGrid)
published
property Align; { propriétés publiées }
property BorderStyle;
property Color;

41-2 Guide du développeur


Modification des valeurs initiales

property Ctl3D;
property Font;
property GridLineWidth;
property ParentColor;
property ParentFont;
property OnClick; { événements publiés }
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEndDrag;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
end;
Il existe bien d’autres propriétés ne s’appliquant pas à un calendrier qui sont
publiables, par exemple la propriété Options qui permet à l’utilisateur de choisir les
lignes de la grille à dessiner.
Si vous installez le composant calendrier modifié dans la palette des composants
et l’utilisez dans une application, vous trouverez bien d’autres propriétés et
événements opérationnels. Nous allons maintenant commencer à ajouter de
nouvelles fonctionnalités au composant.

Modification des valeurs initiales


Un calendrier est essentiellement une grille avec un nombre fixe de lignes et de
colonnes, ne contenant pas nécessairement des dates. Les propriétés ColCount et
RowCount de la grille n’ont donc pas été publiées, car il est peu probable que les
utilisateurs du calendrier voudront afficher autre chose que les sept jours de la
semaine. Vous devez néanmoins définir les valeurs initiales de ces propriétés en
fonction des sept jours de la semaine.
Pour changer les valeurs initiales des propriétés du composant, vous devez
surcharger le constructeur afin qu’il affecte les valeurs voulues. Le constructeur
doit être virtuel.
Souvenez-vous que vous devez ajouter le constructeur à la partie public de la
déclaration de la classe du composant, puis écrire le nouveau constructeur dans
la partie implémentation de l’unité du composant. La première instruction du
nouveau constructeur doit toujours être un appel au constructeur hérité.
type
TSampleCalendar = class(TCustomGrid
public
constructor Create(AOwner: TComponent); override ;
ƒ
end;
ƒ
constructor TSampleCalendar.Create(AOwner: TComponent);

Personnalisation d’une grille 41-3


Redimensionnement des cellules

begin
inherited Create(AOwner); { appelle le constructeur hérité }
ColCount := 7; { toujours 7 jours/semaine }
RowCount := 7; { toujours 6 semaines plus les titres }
FixedCols := 0; { aucun libellé de ligne }
FixedRows := 1; { une ligne pour les noms de jour }
ScrollBars := ssNone; { pas de défilement nécessaire }
Options := Options - [goRangeSelect] + [goDrawFocusSelected]; {désactive la sélection
// d’intervalle}
end;
Le calendrier a dorénavant sept colonnes et sept lignes, avec la ligne de titre fixe
(ou qui ne défile pas).

Redimensionnement des cellules


Lorsqu’un utilisateur ou une application modifie la taille d’une fenêtre ou d’un
contrôle, Windows envoie le message WM_SIZE à la fenêtre ou au contrôle
concerné pour lui permettre d’ajuster les paramètres nécessaires afin de dessiner
ultérieurement son image dans la nouvelle taille. Votre composant peut répondre
à ce message en modifiant la taille des cellules de façon à ce qu’elles s’inscrivent
dans les limites du contrôle. Pour répondre au message WM_SIZE, vous devez
ajouter au composant une méthode de gestion du message.
La création d’une méthode de gestion de message est décrite en détail dans
“Création de nouveaux gestionnaires de messages” à la page 37-5.
Dans notre exemple, le contrôle calendrier devant répondre au message WM_SIZE,
vous devez ajouter au contrôle une méthode protégée appelée WMSize et indexée
par le message WM_SIZE, puis écrire la méthode de calcul de la taille des cellules
qui permettra à toutes d’être visibles :
type
TSampleCalendar = class(TCustomGrid)
protected
procedure WMSize(var Message: TWMSize); message WM_SIZE;
ƒ
end;
ƒ
procedure TSampleCalendar.WMSize(var Message: TWMSize);
var
GridLines: Integer; { variable locale temporaire }
begin
GridLines := 6 * GridLineWidth; { calcule la taille combinée de toutes les lignes }
DefaultColWidth := (Message.Width - GridLines) div 7; { définit la nouvelle largeur
// de cellule par défaut }
DefaultRowHeight := (Message.Height - GridLines) div 7; { ainsi que sa hauteur }
end;
Maintenant lorsque le calendrier est redimensionné, il affiche toutes les cellules
dans la taille maximum avec laquelle ils peuvent rentrer dans le contrôle.

41-4 Guide du développeur


Remplissage des cellules

Remplissage des cellules


Un contrôle grille se remplit cellule par cellule. S’agissant du calendrier, cela
revient à calculer une date (si elle existe) pour chaque cellule. Le dessin par défaut
des cellules de la grille s’opère dans une méthode virtuelle intitulée DrawCell.
Pour remplir le contenu des cellules de la grille, vous devez surcharger la méthode
DrawCell.
Les cellules de titre de la ligne fixe sont ce qu’il y a de plus facile à remplir. La
bibliothèque d’exécution contient un tableau avec l’intitulé raccourci des jours et il
vous faut donc insérer l’intitulé approprié à chaque colonne :
type
TSampleCalendar = class(TCustomGrid)
protected
procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
override ;
end;
ƒ
procedure TSampleCalendar.DrawCell(ACol, ARow: Longint; ARect: TRect;
AState: TGridDrawState);
begin
if ARow = 0 then
Canvas.TextOut(ARect.Left, ARect.Top, ShortDayNames[ACol + 1]); { utilise les chaînes
// RTL }
end;

Suivi de la date
Pour que le contrôle calendrier soit utile, les utilisateurs ainsi que les applications
doivent disposer d’un moyen de définir la date, le mois et l’année. Delphi stocke
les dates et les heures dans des variables de type TDateTime. TDateTime est une
représentation numérique encodée des dates et des heures particulièrement pratique
pour être manipulée par un programme mais peu commode à interpréter par un
utilisateur.
Vous pouvez donc stocker la date du calendrier sous une forme encodée et fournir
un accès direct à cette valeur lors de l’exécution, mais vous pouvez aussi fournir
les propriétés Day, Month et Year que l’utilisateur du composant peut définir lors
de la conception.
Le suivi de la date dans le calendrier comprend les traitements suivants :
• Stockage interne de la date
• Accès au jour, au mois et à l’année
• Génération des numéros de jours
• Sélection du jour en cours

Personnalisation d’une grille 41-5


Remplissage des cellules

Stockage interne de la date


Pour stocker la date du calendrier, vous devez avoir un champ contenant la date,
ainsi qu’une propriété accessible à l’exécution seulement qui fournit un accès à
cette date.
L’ajout de la date interne au calendrier requiert trois étapes :
1 Déclarez une donnée membre privée pour contenir la date :
type
TSampleCalendar = class(TCustomGrid)
private
FDate: TDateTime;
ƒ
2 Initialisez la donnée membre date dans le constructeur ::
constructor TSampleCalendar.Create(AOwner: TComponent);
begin
inherited Create(AOwner); { déjà ici }
ƒ { d’autres initialisations ici }
FDate := Date; { prend la date active du RTL }
end;
3 Déclarez une propriété à l’exécution pour accéder à la date encodée :.
Vous aurez besoin d’une méthode pour définir la date car sa définition entraîne
la mise à jour de l’image sur l’écran du contrôle :
type
TSampleCalendar = class(TCustomGrid)
private
procedure SetCalendarDate(Value: TDateTime);
public
property CalendarDate: TDateTime read FDate write SetCalendarDate;
ƒ
procedure TSampleCalendar.SetCalendarDate(Value: TDateTime);
begin
FDate := Value; { définit la nouvelle valeur date }
Refresh; { met à jour l’image à l’écran }
end;

Accès au jour, au mois et à l’année


Une date encodée numériquement est adaptée aux applications, mais l’utilisateur
préférera manipuler jour, mois et année. En créant des propriétés, vous offrez un
accès aux dates stockées et encodées numériquement.
Les éléments d’une date (jour, mois, année) sont des entiers. La modification de
chacun d’entre eux nécessite l’encodage de la date. Vous pouvez éviter la
duplication du code en partageant les méthodes d’implémentation entre les trois
propriétés. Autrement dit, vous pouvez écrire deux méthodes, l’une pour lire un
élément et l’autre pour l’écrire, et utiliser ces méthodes pour lire et écrire les trois
propriétés.

41-6 Guide du développeur


Remplissage des cellules

Pour fournir un accès lors de la conception aux éléments jour, mois et année,
procédez de la façon suivante :
1 Déclarez les trois propriétés, en attribuant à chacune un numéro unique d’index :
type
TSampleCalendar = class(TCustomGrid)
public
property Day: Integer index 3 read GetDateElement write SetDateElement;
property Month: Integer index 2 read GetDateElement write SetDateElement;
property Year: Integer index 1 read GetDateElement write SetDateElement;
ƒ
2 Déclarez et écrivez les méthodes d’implémentation, définissant les différents
éléments pour chaque valeur d’index :
type
TSampleCalendar = class(TCustomGrid)
private
function GetDateElement(Index: Integer): Integer; { notez le paramètre Index }
procedure SetDateElement(Index: Integer; Value: Integer);
ƒ
function TSampleCalendar.GetDateElement(Index: Integer): Integer;
var
AYear, AMonth, ADay: Word;
begin
DecodeDate(FDate, AYear, AMonth, ADay); { éclate la date encodée en éléments }
case Index of
1: Result := AYear;
2: Result := AMonth;
3: Result := ADay;
else Result := -1;
end;
end;
procedure TSampleCalendar.SetDateElement(Index: Integer; Value: Integer);
var
AYear, AMonth, ADay: Word;
begin
if Value > 0 then { tous les éléments doivent être positifs }
begin
DecodeDate(FDate, AYear, AMonth, ADay); { récupère les éléments courants de la date}
case Index of { définit le nouvel élément selon l’index }
1: AYear := Value;
2: AMonth := Value;
3: ADay := Value;
else Exit;
end;
FDate := EncodeDate(AYear, AMonth, ADay); { encodage de la date modifiée }
Refresh; { mise à jour du calendrier visible }
end;
end;
Vous pouvez maintenant définir le jour, le mois et l’année du calendrier lors de
la conception à partir de l’inspecteur d’objets, ou à l’exécution à partir du code.
Bien que vous n’ayez pas encore ajouté le code pour dessiner les dates dans les
cellules, vous disposez maintenant de toutes les données nécessaires.

Personnalisation d’une grille 41-7


Remplissage des cellules

Génération des numéros de jours


Insérer les numéros des jours dans le calendrier nécessite plusieurs considérations.
Le nombre de jours dans le mois dépend à la fois du mois et de l’année. Le jour de
la semaine qui débute le mois dépend aussi du mois et de l’année. Utilisez la
fonction IsLeapYear pour déterminer si l’année est bissextile. Utilisez le tableau
MonthDays dans l’unité SysUtils pour obtenir le nombre de jours dans le mois.
Une fois récupérées les informations concernant les années bissextiles et le
nombre de jours par mois, vous pouvez calculer l’endroit de la grille où s’insère
chaque date. Le calcul dépend du premier jour du mois.
Comme vous devez considérer le décalage du premier jour du mois, par rapport
à l’origine de la grille, pour chaque cellule à remplir, le meilleur choix consiste à
calculer ce nombre après chaque changement de mois ou d’année, et de s’y reporter
à chaque fois. Vous pouvez stocker cette valeur dans un champ de classe, puis
mettre à jour ce champ à chaque modification de la date.
Pour remplir les cellules avec les numéros de jour appropriés, procédez de la
façon suivante :
1 Ajoutez à la classe une donnée membre décalage du premier jour du mois, ainsi
qu’une méthode pour mettre à jour la valeur de cette donnée membre :
type
TSampleCalendar = class(TCustomGrid)
private
FMonthOffset: Integer; { stocke le décalage }
ƒ
protected
procedure UpdateCalendar; virtual ; { propriété pour l’accès au décalage }
end;
ƒ
procedure TSampleCalendar.UpdateCalendar;
var
AYear, AMonth, ADay: Word;
FirstDate: TDateTime; { date du premier jour du mois }
begin
if FDate <> 0 then { ne calcule le décalage que si la date est valide }
begin
DecodeDate(FDate, AYear, AMonth, ADay); { récupère les éléments de la date }
FirstDate := EncodeDate(AYear, AMonth, 1); { date du premier jour du mois }
FMonthOffset := 2 - DayOfWeek(FirstDate); { génère le décalage dans la grille }
end;
Refresh; { toujours repeindre le contrôle }
end;
2 Ajoutez les instructions au constructeur et aux méthodes SetCalendarDate et
SetDateElement qui appellent la nouvelle méthode de mise à jour à chaque
changement de date :
constructor TSampleCalendar.Create(AOwner: TComponent);
begin
inherited Create(AOwner); { existait déjà }
ƒ { d’autres initialisations ici }
UpdateCalendar; { définit le bon décalage }
end;

41-8 Guide du développeur


Remplissage des cellules

procedure TSampleCalendar.SetCalendarDate(Value: TDateTime);


begin
FDate := Value; { existait déjà }
UpdateCalendar; { appelait précédemment Refresh }
end;
procedure TSampleCalendar.SetDateElement(Index: Integer; Value: Integer);
begin
ƒ
FDate := EncodeDate(AYear, AMonth, ADay); { encode la date modifiée }
UpdateCalendar; { appelait précédemment Refresh }
end;
end;
3 Ajoutez une méthode au calendrier renvoyant le numéro du jour à partir des
coordonnées ligne/colonne d’une cellule qui lui sont transmises :
function TSampleCalendar.DayNum(ACol, ARow: Integer): Integer;
begin
Result := FMonthOffset + ACol + (ARow - 1) * 7; { calcule le jour pour cette cellule}
if (Result < 1) or (Result > MonthDays[IsLeapYear(Year), Month]) then
Result := -1; { renvoie -1 si incorrect }
end;
N’oubliez pas d’ajouter la déclaration de DayNum à la déclaration de type du
composant.
4 Vous pouvez désormais calculer l’endroit où s’affichent les dates, et mettre à
jour DrawCell pour remplir les cellules :
procedure TCalendar.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
var
TheText: string;
TempDay: Integer;
begin
if ARow = 0 then { s’il s’agit de la ligne de titre ...}
TheText := ShortDayNames[ACol + 1] { utilise le nom du jour }
else begin
TheText := ''; { cellule vide par défaut }
TempDay := DayNum(ACol, ARow); { récupère le numéro pour cette cellule }
if TempDay <> -1 then TheText := IntToStr(TempDay); { utilise ce numéro s’il est
// valide }
end;
with ARect, Canvas do
TextRect(ARect, Left + (Right - Left - TextWidth(TheText)) div 2,
Top + (Bottom - Top - TextHeight(TheText)) div 2, TheText);
end;
Si maintenant vous réinstallez le composant calendrier et le placez dans une fiche,
les informations correspondant au mois en cours apparaîtront.

Sélection du jour en cours


Maintenant que les numéros des jours s’affichent dans les cellules du calendrier, il
devient intéressant de savoir positionner la surbrillance sur le jour en cours.
Comme la sélection se positionne implicitement sur la cellule située en haut et à
gauche, vous devez définir les propriétés Row et Column au moment de la
construction initiale du calendrier ainsi qu’à chaque changement de date.

Personnalisation d’une grille 41-9


Navigation de mois en mois et d’année en année

Pour positionner la sélection dans la cellule du jour en cours, modifiez la méthode


UpdateCalendar pour définir Row et Column avant d’appeler Refresh:
procedure TSampleCalendar.UpdateCalendar;
begin
if FDate <> 0 then
begin
ƒ { existing statements to set FMonthOffset }
Row := (ADay - FMonthOffset) div 7 + 1;
Col := (ADay - FMonthOffset) mod 7;
end;
Refresh; { déjà ici }
end;
Notez que vous réutilisez la variable ADay précédemment définie lors du décodage
de la date.

Navigation de mois en mois et d’année en année


Les propriétés sont particulièrement utiles pour manipuler les composants, en
particulier lors de la conception. Mais lorsque des manipulations fréquentes ou
instinctives font intervenir plusieurs propriétés, il paraît judicieux de fournir des
méthodes pour les gérer. Le passage au “mois suivant” dans notre calendrier est un
exemple de ce type. Le bouclage sur les douze mois avec l’incrémentation de
l’année est à la fois une caractéristique simple et commode pour le programmeur
qui utilise le composant.
Le seul inconvénient à l’encapsulation des manipulations les plus fréquentes sous la
forme de méthodes est le suivant : les méthodes ne sont accessibles qu’à l’exécution.
Néanmoins, de telles manipulations ne sont fastidieuses que lorsqu’elles sont
souvent répétées, ce qui est rarement le cas au moment de la conception.
S’agissant du calendrier, ajoutez les quatre méthodes suivantes pour gérer le
passage de mois en mois et d’année en année. Chacune de ces méthodes utilise la
fonction IncMonth de façon légèrement différente pour incrémenter ou
décrémenter CalendarDate de mois en mois ou d’année en année. Après avoir
incrémenté ou décrémenté CalendarDate, décodez la valeur de la date pour
remplir les propriétés Year, Month et Day avec les nouvelles valeurs
correspondantes .
procedure TCalendar.NextMonth;
begin
DecodeDate(IncMonth(CalendarDate, 1), Year, Month, Day);
end;
procedure TCalendar.PrevMonth;
begin
DecodeDate(IncMonth(CalendarDate, -1), Year, Month, Day);
end;
procedure TCalendar.NextYear;
begin
DecodeDate(IncMonth(CalendarDate, 12), Year, Month, Day);
end;

41-10 Guide du développeur


Navigation de jour en jour

procedure TCalendar.PrevYear;
begin
DecodeDate(CalendarDate, -12), Year, Month, Day);
end;
N’oubliez pas d’ajouter les déclarations des nouvelles méthodes à la déclaration de
la classe.
Désormais, si vous créez une application qui utilise le composant calendrier, vous
pourrez facilement implémenter le passage de mois en mois ou d’année en année.

Navigation de jour en jour


A l’intérieur d’un même mois, il existe deux moyens évidents pour naviguer
parmi les jours. Le premier consiste à utiliser les touches de direction et le
deuxième à répondre aux clics de la souris. Le composant grille standard les
gère tous les deux indistinctement en tant que clics de souris. Autrement dit, le
déplacement avec les touches de direction est pris en compte comme un clic sur
une cellule adjacente.
Le processus de navigation de jour en jour comprend :
• Déplacement de la sélection
• Fourniture d’un événement OnChange
• Exclusion des cellules vides

Déplacement de la sélection
Le comportement reçu en héritage d’une grille gère le déplacement de la sélection
en réponse aux touches de direction enfoncées ou aux clics de souris. Pour modifier
le jour sélectionné, vous devez modifier le comportement implicite.
Pour gérer les déplacements à l’intérieur du calendrier, vous devez surcharger la
méthode Click de la grille.
Lorsque vous surchargez une méthode telle que Click, en dépendance étroite avec
les interactions de l’utilisateur, vous devez pratiquement toujours inclure un appel à
la méthode reçue en héritage pour ne pas perdre le comportement standard.
Le code suivant est une méthode Click surchargée pour la grille calendrier.
N’oubliez pas d’ajouter la déclaration de Click à TSampleCalendar, en incluant après
la directive override.
procedure TSampleCalendar.Click;
var
TempDay: Integer;
begin
inherited Click; { n’oubliez pas d’appeler la méthode héritée ! }
TempDay := DayNum(Col, Row); { récupère le numéro du jour de la cellule cliquée }
if TempDay <> -1 then Day := TempDay; { change le jour s’il est valide }
end;

Personnalisation d’une grille 41-11


Navigation de jour en jour

Fourniture d’un événement OnChange


Les utilisateurs de votre calendrier ont maintenant la possibilité de changer la date.
Il paraît donc judicieux de répondre à ces changements.
Ajoutez un événement OnChange à TSampleCalendar.
1 Déclarez l’événement ainsi qu’un champ pour le stocker et une méthode virtuelle
pour l’appeler :
type
TSampleCalendar = class(TCustomGrid)
private
FOnChange: TNotifyEvent;
protected
procedure Change; dynamic;
ƒ
published
property OnChange: TNotifyEvent read FOnChange write FOnChange;
ƒ
2 Ecrivez la méthode Change :
procedure TSampleCalendar.Change;
begin
if Assigned(FOnChange) then FOnChange(Self);
end;
3 Ajoutez les instructions appelant Change à la fin des méthodes SetCalendarDate et
SetDateElement :
procedure TSampleCalendar.SetCalendarDate(Value: TDateTime);
begin
FDate := Value;
UpdateCalendar;
Change; { seule nouvelle instruction }
end;
procedure TSampleCalendar.SetDateElement(Index: Integer; Value: Integer);
begin
ƒ { instructions définissant les valeurs des éléments }
FDate := EncodeDate(AYear, AMonth, ADay);
UpdateCalendar;
Change; { ceci est nouveau }
end;
end;
Les applications qui utilisent le composant calendrier peuvent maintenant répondre
aux changements en associant des gestionnaires à l’événement OnChange.

Exclusion des cellules vides


Tel qu’il est actuellement, le calendrier déplace la sélection vers une cellule vide
sans changer la date. Il devient intéressant d’empêcher la sélection des cellules
vides.

41-12 Guide du développeur


Navigation de jour en jour

Pour déterminer si une cellule est sélectionnable, vous devez surcharger la méthode
SelectCell de la grille.
SelectCell est une fonction qui accepte deux paramètres ligne et colonne et qui
renvoie une valeur booléenne indiquant si la cellule spécifiée est sélectionnable.
Vous pouvez surcharger SelectCell pour qu’elle renvoie false si la cellule ne contient
pas une date valide :
function TSampleCalendar.SelectCell(ACol, ARow: Longint): Boolean;
begin
if DayNum(ACol, ARow) = -1 then Result := False { -1 indique une date incorrecte }
else Result := inherited SelectCell(ACol, ARow); { sinon, utilise la valeur héritée }
end;
Désormais, si l’utilisateur clique sur une cellule vide ou tente de s’y déplacer à
l’aide des touches de direction, le calendrier ne modifie pas la sélection en cours.

Personnalisation d’une grille 41-13


41-14 Guide du développeur
Chapitre

Contrôles orientés données


Chapter 42
42
Lorsque vous souhaitez vous connecter avec des bases de données, travaillez
avec les contrôles orientés données. C’est grâce à ces contrôles que l’application
établit un lien avec une partie spécifique d’une base de données. Parmi les
contrôles sensibles aux données de Delphi, citons les libellés, les boîtes de saisie,
les boîtes liste, les boîtes à options, les contrôles de référence et les grilles. Vous
avez également la possibilité de construire vos propres contrôles orientés
données. Pour plus d’informations sur l’utilisation des contrôles orientés
données, voir chapitre 26, “Utilisation de contrôles de données”.
Il existe différents niveaux d’orientation données. Le plus élémentaire fonctionne
en lecture seulement, permet de scruter des données et reflète l’état d’une base de
données. L’orientation données modifiables, permettant de modifier les données, est
plus complexe car l’utilisateur peut changer les valeurs stockées dans la base en
manipulant le contrôle. Notez également que le degré d’implication de la base de
données peut varier du cas le plus simple, un lien établi avec un seul champ,
aux cas plus complexes faisant intervenir des contrôles à enregistrements
multiples.
Ce chapitre illustre d’abord le cas le plus simple, en créant un contrôle en lecture
simple qui est lié à un seul champ d’un ensemble de données. Le contrôle
spécifique utilisé sera le calendrier créé dans le chapitre 41, “Personnalisation
d’une grille”, TSampleCalendar. Vous pouvez aussi utiliser le contrôle calendrier
standard de la page Exemples de la palette des composants, TCalendar.
Ce chapitre continue ensuite avec une explication sur la manière de faire d’un
nouveau contrôle pour scruter les données un contrôle de modification des
données.

Contrôles orientés données 42-1


Création d’un contrôle pour scruter les données

Création d’un contrôle pour scruter les données


La création d’un contrôle calendrier orienté données, que ce soit un contrôle en
lecture seulement ou un contrôle grâce auquel l’utilisateur peut changer les
données sous-jacentes, fait intervenir les étapes suivantes :
• Création et recensement du composant.
• Ajout du lien aux données.
• Réponse aux changements de données.

Création et recensement du composant


La création d’un composant débute toujours de la même façon. Vous créez une unité et
vous recensez le composant avant de l’installer dans la palette des composants. Ce
processus est décrit dans “Création d’un nouveau composant” à la page 31-8.
Pour notre exemple, suivez la procédure générale de création d’un composant en
tenant compte des spécificités suivantes :
• Appelez l’unité du composant DBCal.
• Dérivez une nouvelle classe composant appelée TDBCalendar, descendante de
TSampleCalendar. Le chapitre 41, “Personnalisation d’une grille” montre
comment créer le composant TSampleCalendar.
• Recensez TDBCalendar dans la page Exemples de la palette des composants.
L’unité que vous obtenez doit ressembler à ceci :
unit DBCal;
interface
uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Grids, Calendar;
type
TDBCalendar = class(TSampleCalendar)
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TDBCalendar]);
end;
end.
Vous pouvez à présent commencer à transformer le nouveau calendrier en
scruteur de données.

42-2 Guide du développeur


Création d’un contrôle pour scruter les données

Fonctionnement du contrôle en lecture seulement


Puisque votre calendrier scruteur de données ne fonctionnera qu’en lecture (par
rapport aux données), il est opportun de rendre le contrôle lui-même accessible
en lecture seulement. Ainsi, l’utilisateur ne s’attendra pas à voir répercuter dans
la base de données une modification qu’il aurait apporté au contrôle.
Rendre le calendrier accessible en lecture seulement fait intervenir deux étapes :
• Ajout de la propriété ReadOnly.
• Autorisation des mises à jour nécessaires.
Si vous démarrez avec le composant TCalendar de la page Exemples de Delphi
au lieu de TSampleCalendar, le contrôle a déjà une propriété ReadOnly. Vous
pouvez donc ignorer ces étapes.

Ajout de la propriété ReadOnly


En ajoutant une propriété ReadOnly, vous fournissez le moyen de rendre le
contrôle accessible en lecture seulement au moment de la conception. Si la valeur
de cette propriété est True, toutes les cellules du contrôle perdront la capacité à
être sélectionnées.
1 Ajoutez la déclaration de la propriété ainsi qu’une donnée membre private
pour contenir la valeur :
type
TDBCalendar = class(TSampleCalendar)
private
FReadOnly: Boolean; { champ de stockage interne }
public
constructor Create(AOwner: TComponent); override ; { doit surcharger pour définir
// les valeurs par défaut }
published
property ReadOnly: Boolean read FReadOnly write FReadOnly default True;
end;
ƒ
constructor TDBCalendar.Create(AOwner: TComponent);
begin
inherited Create(AOwner); { appelez toujours le constructeur hérité ! }
FReadOnly := True; { définit la valeur par défaut }
end;
2 Surchargez la méthode SelectCell pour inhiber la sélection si le contrôle est
accessible en lecture seulement. L’utilisation de SelectCell est expliquée dans
“Exclusion des cellules vides” à la page 41-12.
function TDBCalendar.SelectCell(ACol, ARow: Longint): Boolean;
begin
if FReadOnly then Result := False { sélection impossible si accès en
// lecture seule }
else Result := inherited SelectCell(ACol, ARow); { sinon, utilise la méthode héritée }
end;

Contrôles orientés données 42-3


Création d’un contrôle pour scruter les données

N’oubliez pas d’ajouter la déclaration de SelectCell à la déclaration de la classe de


TDBCalendar, et ajoutez la directive override.
Si vous ajoutez maintenant le calendrier à une fiche, vous vous rendez compte
que le composant ignore les clics de souris et les frappes de touches. Et il ne met
plus à jour la position de la sélection lorsque vous changez la date.

Autorisation des mises à jour nécessaires


Le calendrier accessible en lecture seulement utilise la méthode SelectCell pour
effectuer toutes sortes de modifications, y compris l’affectation de valeurs aux
propriétés Row et Col. La méthode UpdateCalendar définit Row et Col à chaque
changement de date mais, dans la mesure où SelectCell n’autorise aucune
modification, la position de la sélection reste inchangée, même si la date est
modifiée.
Pour outrepasser cette interdiction de toute modification, vous devez ajouter au
calendrier un indicateur booléen interne et n’autoriser les modifications que si la
valeur de cet indicateur est true :
type
TDBCalendar = class(TSampleCalendar)
private
FUpdating: Boolean; { indicateur privé à usage interne }
protected
function SelectCell(ACol, ARow: Longint): Boolean; override ;
public
procedure UpdateCalendar; override ; { notez la directive override }
end;
ƒ
function TDBCalendar.SelectCell(ACol, ARow: Longint): Boolean;
begin
if (not FUpdating) and FReadOnly then Result := False { sélection possible si mise
// à jour }
else Result := inherited SelectCell(ACol, ARow); { sinon, utilise la méthode héritée }
end;
procedure TDBCalendar.UpdateCalendar;
begin
FUpdating := True; { définit l’indicateur pour permettre les mises à jour }
try
inherited UpdateCalendar; { mise à jour habituelle }
finally
FUpdating := False; { réinitialise toujours l’indicateur }
end;
end;
Le calendrier n’autorise toujours pas les modifications directes de l’utilisateur
mais les modifications de la date effectuées via les propriétés de date sont prises
en compte. Vous disposez maintenant d’un contrôle en lecture seulement tout à
fait opérationnel. Vous voilà prêt à ajouter les fonctionnalités servant à scruter
les données.

42-4 Guide du développeur


Création d’un contrôle pour scruter les données

Ajout du lien aux données


La connexion entre un contrôle et une base de données est gérée par une classe
appelée lien de données. La classe lien de données, qui connecte un contrôle à un
seul champ d’une base de données, est TFieldDataLink. Il existe également des
liens de données vers des tables entières.
Un contrôle orienté données est propriétaire de sa classe lien de données.
Autrement dit, le contrôle est responsable de la construction et de la destruction
du lien de données. Pour des détails sur la gestion des classes ayant un
propriétaire, voir chapitre 40, “Création d’un composant graphique”.
Pour établir un lien de données en tant que classe ayant un propriétaire,
respectez ces étapes :
1 Déclaration du champ de classe
2 Déclaration des propriétés d’accès
3 Initialisation du lien de données

Déclaration du champ de classe


Un composant utilise un champ pour chacune des classes dont il est le
propriétaire, comme cela est expliqué dans “Déclaration des champs de classe” à
la page 40-5. Dans ce cas, le calendrier a besoin d’un champ de type
TFieldDataLink pour son lien de données.
Déclarez un champ pour le lien de données du calendrier :
type
TDBCalendar = class(TSampleCalendar)
private
FDataLink: TFieldDataLink;
ƒ
end;
Avant de compiler l’application, vous devez ajouter DB et DBCtrls à la clause
uses de l’unité.

Déclaration des propriétés d’accès


Tout contrôle orienté données dispose d’une propriété DataSource indiquant la
classe source de données qui fournit les données au contrôle. En outre, un
contrôle qui accède à un champ unique a besoin d’une propriété DataField pour
spécifier ce champ dans la source de données.
Contrairement aux propriétés d’accès des classes ayant un propriétaire que nous
avons vues avec l’exemple dans le chapitre 40, “Création d’un composant
graphique”, ces propriétés d’accès ne donnent pas accès aux classes ayant un
propriétaire elles-mêmes, mais plutôt aux propriétés correspondantes de la classe
ayant un propriétaire. Autrement dit, vous allez créer des propriétés qui
autorisent le contrôle et son lien de données à partager la même source et le
même champ.

Contrôles orientés données 42-5


Création d’un contrôle pour scruter les données

Déclarez les propriétés DataSource et DataField ainsi que leurs méthodes


d’implémentation, puis écrivez ces méthodes en tant que simples “boîtes à
lettres” vers les propriétés correspondantes de la classe lien de données :

Exemple de déclaration des propriétés d’accès


type
TDBCalendar = class(TSampleCalendar)
private { les méthodes d’implémentation sont private }
...
function GetDataField: string; { renvoie le nom du champ de données }
function GetDataSource: TDataSource; { renvoie une référence sur la source de données }
procedure SetDataField(const Value: string); { affecte le nom du champ de données }
procedure SetDataSource(Value: TDataSource); { affecte une nouvelle source de données }
published { rend les propriétés accessibles lors de la conception }
property DataField: string read GetDataField write SetDataField;
property DataSource: TDataSource read GetDataSource write SetDataSource;
end;
ƒ
function TDBCalendar.GetDataField: string;
begin
Result := FDataLink.FieldName;
end;
function TDBCalendar.GetDataSource: TDataSource;
begin
Result := FDataLink.DataSource;
end;
procedure TDBCalendar.SetDataField(const Value: string);
begin
FDataLink.FieldName := Value;
end;
procedure TDBCalendar.SetDataSource(Value: TDataSource);
begin
FDataLink.DataSource := Value;
end;
Maintenant que sont établis les liens entre le calendrier et son lien de données, il
reste une étape importante à franchir. Vous devez construire la classe lien de
données au moment de la construction du contrôle calendrier et le détruire avant
de détruire ce même contrôle.

Initialisation du lien de données


Un contrôle orienté données doit avoir accès à son lien de données pendant
toute sa durée de vie, il doit donc construire l’objet lien de données dans son
propre constructeur et le détruire avant de se détruire lui-même.

42-6 Guide du développeur


Création d’un contrôle pour scruter les données

Surchargez les méthodes Create et Destroy du calendrier pour construire et


détruire l’objet lien de données ::
type
TDBCalendar = class(TSampleCalendar)
public { les constructeurs et destructeurs sont toujours publics }
constructor Create(AOwner: TComponent); override ;
destructor Destroy; override ;
ƒ
end;
ƒ
constructor TDBCalendar.Create(AOwner: TComponent);
begin
FDataLink := TFieldDataLink.Create; { construit l’objet lien de données }
inherited Create(AOwner); { appelle toujours d’abord le constructeur hérité }
FReadOnly := True; { existe déjà }
end;
destructor TDBCalendar.Destroy;
begin
FDataLink.Free; { détruit toujours d’abord les objets ayant un propriétaire... }
inherited Destroy; { ...puis appelle le destructeur hérité }
end;
Vous avez maintenant un lien de données complet. Il vous reste à indiquer au
contrôle les données qu’il doit lire dans le champ lié. La section suivante vous
explique comment procéder.

Réponse aux changements de données


Lorsqu’un contrôle a un lien de données et les propriétés précisant la source et
le champ des données, il doit répondre aux changements des données de ce
champ provoqués soit par un déplacement vers un autre enregistrement, soit par
une modification du champ.
Les classes lien de données ont toutes un événement intitulé OnDataChange.
Lorsque la source de données indique un changement dans ses données, l’objet
lien de données appelle le gestionnaire attaché à son événement OnDataChange.
Pour mettre à jour un contrôle en réponse à une modification des données, vous
devez attacher un gestionnaire à l’événement OnDataChange du lien de données.
Dans notre exemple, vous allez ajouter une méthode au calendrier, puis la
désigner comme gestionnaire de l’événement OnDataChange du lien de données.
Déclarez et implémentez la méthode DataChange, puis associez-la à l’événement
OnDataChange dans le constructeur. Dans le destructeur, détachez le gestionnaire
OnDataChange avant de détruire l’objet.
type
TDBCalendar = class(TSampleCalendar)
private { this is an internal detail, so make it private }
procedure DataChange(Sender: TObject); { doit avoir des paramètres corrects pour
// l’événement }
end;
ƒ

Contrôles orientés données 42-7


Création d’un contrôle pour modifier les données

constructor TDBCalendar.Create(AOwner: TComponent);


begin
inherited Create(AOwner); { appelle toujours d’abord le constructeur hérité }
FReadOnly := True; { existe déjà }
FDataLink := TFieldDataLink.Create; { construit l’objet lien de données }
FDataLink.OnDataChange := DataChange; { attache le gestionnaire à l’événement }
end;
destructor TDBCalendar.Destroy;
begin
FDataLink.OnDataChange := nil; { détache le gestionnaire avant de détruire l’objet }
FDataLink.Free; { détruit toujous d’abord les objets ayant un propriétaire... }
inherited Destroy; { ...puis appelle le destructeur hérité }
end;
procedure TDBCalendar.DataChange(Sender: TObject);
begin
if FDataLink.Field = nil then { s’il n’y a pas de champ attribué... }
CalendarDate := 0 { ...définit une date incorrecte }
else CalendarDate := FDataLink.Field.AsDateTime; { sinon, définit le calendrier par la
// date }
end;
Vous avez maintenant un contrôle de parcours des données.

Création d’un contrôle pour modifier les données


Lorsque vous créez un contrôle permettant de modifier les données, vous créez
et recensez le composant puis lui ajoutez un lien de données, comme pour les
contrôles permettant de scruter les données. Vous devez également répondre aux
changements de données dans le champ sous-jacent, mais vous devez prendre en
considération quelques points supplémentaires.
Par exemple, vous souhaitez sans doute que votre contrôle réponde aux
événements clavier et souris. Votre contrôle doit répondre lorsque l’utilisateur
change le contenu du contrôle. Lorsque l’utilisateur quitte le contrôle, les
changements effectués dans le contrôle doivent être répercutés dans l’ensemble
de données.
Le contrôle permettant la modification des données décrit ici est le même que le
contrôle calendrier décrit dans la première partie de ce chapitre, mais modifié de
telle sorte qu’il permette l’édition en plus de la consultation des données du
champ lié.
Voici les étapes à suivre pour modifier un contrôle existant et en faire un
contrôle permettant la modification des données :
• Modification de la valeur par défaut de FReadOnly.
• Gestion des messages liés à la souris ou au clavier.
• Mise à jour de la classe lien de données sur un champ.
• Modification de la méthode Change.
• Mise à jour de l’ensemble de données.

42-8 Guide du développeur


Création d’un contrôle pour modifier les données

Modification de la valeur par défaut de FReadOnly


Comme il s’agit d’un contrôle permettant la modification des données, la
propriété ReadOnly doit être False par défaut. Pour qu’elle soit False, modifiez la
valeur de FReadOnly dans le constructeur :
constructor TDBCalendar.Create(AOwner: TComponent);
begin
ƒ
FReadOnly := False; { définit la valeur par défaut }
ƒ
end;

Gestion des messages liés à la souris ou au clavier


Lorsque l’utilisateur commence à se servir du contrôle, celui-ci reçoit de
Windows les messages indiquant la manipulation de la souris
(WM_LBUTTONDOWN, WM_MBUTTONDOWN, ou WM_RBUTTONDOWN), ou
le message indiquant la manipulation du clavier (WM_KEYDOWN). Pour
permettre à un contrôle de répondre à ces messages, vous devez écrire les
gestionnaires des réponses à ces messages.
• Réponse aux messages indiquant la manipulation de la souris
• Réponse aux messages indiquant la manipulation du clavier

Réponse aux messages indiquant la manipulation de la souris


Une méthode MouseDown est une méthode protégée de l’événement
OnMouseDown d’un contrôle. Le contrôle lui-même appelle MouseDown en
réponse au message Windows indiquant la manipulation de la souris. Lorsque
vous surchargez la méthode MouseDown héritée, vous pouvez inclure du code
apportant d’autres réponses en plus de l’appel à l’événement OnMouseDown.
Pour surcharger MouseDown, ajoutez la méthode MouseDown à la classe
TDBCalendar :
type
TDBCalendar = class(TSampleCalendar);
ƒ
protected
procedure MouseDown(Button: TButton, Shift: TShiftState, X: Integer, Y: Integer);
override ;
ƒ
end;
procedure TDBCalendar.MouseDown(Button: TButton; Shift: TShiftState; X, Y: Integer);
var
MyMouseDown: TMouseEvent;
begin
if not ReadOnly and FDataLink.Edit then
inherited MouseDown(Button, Shift, X, Y)
else

Contrôles orientés données 42-9


Création d’un contrôle pour modifier les données

begin
MyMouseDown := OnMouseDown;
if Assigned(MyMouseDown then MyMouseDown(Self, Button, Shift, X, Y);
end;
end;
Lorsque MouseDown répond à un message indiquant la manipulation de la
souris, la méthode MouseDown héritée est appelée uniquement si la propriété
ReadOnly du contrôle est False et si l’objet lien de données est en mode édition,
c’est-à-dire si le champ peut être modifié. Si le champ ne peut être modifié, le
code mis par le programmeur dans le gestionnaire de l’événement
OnMouseDown, s’il en existe un, est exécuté.

Réponse aux messages indiquant la manipulation du clavier


Une méthode KeyDown est une méthode protégée de l’événement OnKeyDown
d’un contrôle. Le contrôle lui-même appelle KeyDown en réponse au message
Windows indiquant la manipulation du clavier. Lorsque vous surchargez la
méthode KeyDown héritée, vous pouvez inclure le code qui apporte d’autres
réponses en plus de l’appel à l’événement OnKeyDown.
Pour surcharger KeyDown, suivez ces instructions :
1 Ajoutez une méthode KeyDown à la classe TDBCalendar :
type
TDBCalendar = class(TSampleCalendar);
ƒ
protected
procedure KeyDown(var Key: Word; Shift: TShiftState; X: Integer; Y: Integer);
override ;
ƒ
end;
2 Implémentez la méthode KeyDown :
procedure KeyDown(var Key: Word; Shift: TShiftState);
var
MyKeyDown: TKeyEvent;
begin
if not ReadOnly and (Key in [VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT, VK_END,
VK_HOME, VK_PRIOR, VK_NEXT]) and FDataLink.Edit then
inherited KeyDown(Key, Shift)
else
begin
MyKeyDown := OnKeyDown;
if Assigned(MyKeyDown) then MyKeyDown(Self, Key, Shift);
end;
end;
Lorsque KeyDown répond à un message indiquant la manipulation du clavier, la
méthode KeyDown héritée est appelée uniquement si la propriété ReadOnly du
contrôle vaut False, si la touche appuyée est une des touches de déplacement du
curseur et si l’objet lien de données est en mode édition, c’est-à-dire si le champ
peut être modifié. Si le champ ne peut être modifié ou si une autre touche a été
pressée, le code mis par le programmeur dans le gestionnaire de l’événement
OnKeyDown, s’il en existe un, est exécuté.

42-10 Guide du développeur


Création d’un contrôle pour modifier les données

Mise à jour de la classe lien de données sur un champ


Il existe deux types de modification des données :
• Le changement de la valeur d’un champ doit se répercuter dans le contrôle
orienté données
• Le changement dans le contrôle orienté données doit se répercuter dans la
valeur du champ
Le composant TDBCalendar a déjà une méthode DataChange qui gère les
modifications de la valeur du champ dans l’ensemble de données en assignant
cette valeur à la propriété CalendarDate. La méthode DataChange est le
gestionnaire de l’événement OnDataChange. Ainsi le composant calendrier est
capable de gérer le premier type de modification des données.
De manière semblable, la classe lien de données sur un champ a aussi un
événement OnUpdateData qui se produit lorsque l’utilisateur modifie le contenu
du contrôle orienté données. Le contrôle calendrier a une méthode UpdateData
qui devient le gestionnaire de l’événement OnUpdateData. UpdateData assigne au
champ lien de données la valeur modifiée dans le contrôle orienté données.
1 Pour répercuter dans la valeur du champ une modification effectuée sur la
valeur du calendrier, ajoutez une méthode UpdateData à la section private du
composant calendrier :
type
TDBCalendar = class(TSampleCalendar);
private
procedure UpdateData(Sender: TObject);
ƒ
end;
2 Implémentez la méthode UpdateData :
procedure UpdateData(Sender: TObject);
begin
FDataLink.Field.AsDateTime := CalendarDate; { définit le champ lien par la date du
calendrier }
end;
3 Dans le constructeur de TDBCalendar, affectez la méthode UpdateData à
l’événement OnUpdateData :
constructor TDBCalendar.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FReadOnly := True;
FDataLink := TFieldDataLink.Create;
FDataLink.OnDataChange := DataChange;
FDataLink.OnUpdateData := UpdateData;
end;

Contrôles orientés données 42-11


Création d’un contrôle pour modifier les données

Modification de la méthode Change


La méthode Change du TDBCalendar est appelée chaque fois qu’est définie une
nouvelle valeur de date. Change appelle le gestionnaire de l’événement OnChange,
s’il existe. L’utilisateur du composant peut écrire du code dans le gestionnaire de
l’événement OnChange afin de répondre aux modifications de la date.
Lorsque la date du calendrier change, l’ensemble de données sous-jacent doit
être averti de ce changement. Vous pouvez le faire en surchargeant la méthode
Change et en ajoutant une ligne de code de plus. Voici les étapes à suivre :
1 Ajoutez une nouvelle méthode Change au composant TDBCalendar :
type
TDBCalendar = class(TSampleCalendar);
private
procedure Change; override ;
ƒ
end;
2 Ecrivez la méthode Change, appelant la méthode Modified qui informe
l’ensemble de données que celles-ci ont changé, puis appelle la méthode
Change héritée :
TDBCalendar.Change;
begin
FDataLink.Modified; { appelle la méthode Modified }
inherited Change; { appelle la méthode Change héritée }
end;

Mise à jour de l’ensemble de données


A ce point, une modification dans le contrôle orienté données a changé les
valeurs dans la classe du lien de données sur un champ. La dernière étape de la
création d’un contrôle permettant la modification des données consiste à mettre à
jour l’ensemble de données avec la nouvelle valeur. Cela doit se produire après
que la personne ayant changé la valeur du contrôle quitte ce contrôle en cliquant
à l’extérieur de celui-ci ou en appuyant sur la touche.
La VCL possède des ID de message définis pour les opérations sur les contrôles.
Par exemple, le message CM_EXIT est envoyé au contrôle lorsque l’utilisateur
quitte celui-ci. Vous pouvez écrire un gestionnaire qui réponde à ce message. Et
ensuite, lorsque l’utilisateur quitte le contrôle, la méthode CMExit, gestionnaire
du message CM_EXIT, répondra en mettant à jour l’enregistrement dans
l’ensemble de données avec les valeurs modifiées dans la classe lien de données
sur un champ. Pour plus d’informations sur les gestionnaires de messages, voir
chapitre 37, “Gestion des messages”.

42-12 Guide du développeur


Création d’un contrôle pour modifier les données

Pour mettre à jour l’ensemble de données depuis un gestionnaire de message,


suivez ces instructions :
1 Ajoutez le gestionnaire de message au composant TDBCalendar :
type
TDBCalendar = class(TSampleCalendar);
private
procedure CMExit(var Message: TWMNoParams); message CM_EXIT;
ƒ
end;
2 Implémentez la méthode CMExit afin qu’elle ressemble à ceci :
procedure TDBCalendar.CMExit(var Message: TWMNoParams);
begin
try
FDataLink.UpdateRecord; { indique au lien de données de mettre à jour la base }
except
on Exception do SetFocus; { si échec, ne pas perdre la focalisation }
end;
inherited ;
end;

Contrôles orientés données 42-13


42-14 Guide du développeur
Chapitre

Transformation d’une boîte


Chapter 43
43
de dialogue en composant
Il est pratique de transformer une boîte de dialogue fréquemment sollicitée en un
composant que vous pourrez ajouter dans la palette des composants. Ainsi, vos
composants boîte de dialogue fonctionneront exactement comme ceux des boîtes
de dialogue standard de Windows. L’objectif ici est de créer un composant
simple qu’un utilisateur peut ajouter à un projet et dont il peut définir les
propriétés lors de la conception.
La transformation d’une boîte de dialogue en composant nécessite les étapes
suivantes :
1 Définition de l’interface du composant
2 Création et recensement du composant
3 Création de l’interface du composant
4 Test du composant
A l’exécution, le composant “enveloppe” de Delphi, associé à la boîte de
dialogue crée et exécute celle-ci en lui transmettant les données spécifiées par
l’utilisateur. Le composant boîte de dialogue est donc à la fois réutilisable et
personnalisable.
Dans ce chapitre, vous allez voir comment créer un composant enveloppe autour
de la fiche générique A propos de... disponible dans le référentiel d’objets de
Delphi.
Remarque Copiez les fichiers ABOUT.PAS et ABOUT.DFM dans votre répertoire de travail.
Il n’y a pas grand chose à dire concernant la conception de la boîte de dialogue
enveloppée par un composant. Dans un tel contexte, n’importe quelle fiche peut
fonctionner comme une boîte de dialogue.

Transformation d’une boîte de dialogue en composant 43-1


Définition de l’interface du composant

Définition de l’interface du composant


Avant de créer le composant pour votre boîte de dialogue, vous devez décider
de la façon dont il sera utilisé par les développeurs. Vous devez créer une
interface entre votre boîte de dialogue et les applications qui l’utilisent.
Par exemple, considérons les propriétés des composants associés aux boîtes de
dialogue standard. Elles autorisent le développeur à définir l’état initial de la boîte
de dialogue, tel que le titre ou le paramétrage initial des contrôles, et renvoient
toutes les informations nécessaires lorsque la boîte de dialogue se ferme. Les
seules interactions directes ne se produisent qu’avec les propriétés du composant
enveloppe et pas avec les contrôles individuels de la boîte de dialogue.
L’interface doit donc contenir suffisamment d’informations pour que la fiche
boîte de dialogue s’affiche selon les indications du développeur et qu’elle renvoie
les informations nécessaires à l’application. Les propriétés du composant
enveloppe peuvent être vues comme les données permanentes d’une boîte de
dialogue transitoire.
Dans le cas de votre boîte A propos de, aucune information n’est renvoyée. Les
propriétés de l’enveloppe contiennent donc uniquement les informations
nécessaires pour afficher correctement la boîte A propos de. Puisqu’il y a quatre
champs distincts dans cette boîte sur lesquels l’application peut agir, il vous faut
fournir quatre propriétés de type chaîne pour les paramétrer.

Création et recensement du composant


La création d’un composant débute toujours de la même façon. Vous créez une
unité et vous recensez le composant avant de l’installer dans la palette des
composants. Ce processus est décrit dans “Création d’un nouveau composant” à
la page 31-8.
Pour notre exemple, suivez la procédure générale de création d’un composant en
tenant compte des spécificités suivantes :
• Nommez l’unité du composant AboutDlg.
• Dérivez un nouveau type de composant appelé TAboutBoxDlg, descendant de
TComponent.
• Recensez TAboutBoxDlg sur la page Exemples de la palette des composants.
L’unité que vous obtenez doit ressembler à ceci :
unit AboutDlg;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms;
type
TAboutBoxDlg = class(TComponent)
end;
procedure Register;

43-2 Guide du développeur


Création de l’interface du composant

implementation
procedure Register;
begin
RegisterComponents('Samples', [TAboutBoxDlg]);
end;
end.
Pour l’instant, le nouveau composant possède uniquement les fonctionnalités
intégrées à TComponent. C’est le composant non visuel le plus simple. Dans la
section suivante, vous allez créer l’interface entre le composant et la boîte de
dialogue.

Création de l’interface du composant


Voici les étapes nécessaires à la création de l’interface du composant :
1 Inclusionde l’unité de la fiche
2 Ajout des propriétés de l’interface
3 Ajout de la méthode Execute

Inclusionde l’unité de la fiche


Pour que votre composant enveloppe puisse initialiser et afficher la boîte de
dialogue enveloppée, vous devez ajouter les fichiers de l’unité de la fiche dans la
clause uses de l’unité du composant enveloppe.
Ajoutez About à la clause uses de l’unité AboutDlg.
La clause uses ressemble maintenant à ceci :
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms
About;
L’unité fiche déclare toujours une instance de la classe de la fiche. Dans le cas de
la boîte A propos de, la classe de la fiche est TAboutBox, et l’unité About doit
inclure la déclaration suivante :
var
AboutBox: TAboutBox;
Ainsi en ajoutant About à la clause uses, vous rendez disponible AboutBox au
composant enveloppe.

Ajout des propriétés de l’interface


Avant de poursuivre, vous devez déterminer les propriétés de votre composant
enveloppe nécessaires pour permettre aux développeurs d’utiliser votre boîte de
dialogue en tant que composant dans leurs applications. Puis, ajoutez les
déclarations de ces propriétés à la déclaration de classe du composant.

Transformation d’une boîte de dialogue en composant 43-3


Création de l’interface du composant

Les propriétés d’un composant enveloppe sont sensiblement plus simples à écrire
que celles d’un composant standard. Souvenez-vous que vous ne faites que créer
des données permanentes que l’enveloppe et la boîte de dialogue peuvent
échanger. En définissant ces données sous la forme de propriétés, vous donnez
aux développeurs la possibilité de définir des données au moment de la
conception qui, lors de l’exécution, seront transmises par l’enveloppe à la boîte
de dialogue.
La déclaration d’une propriété d’interface nécessite deux ajouts à la déclaration
de classe du composant :
• Un champ de classe privé qui est une variable utilisée par l’enveloppe pour
stocker la valeur de la propriété
• La déclaration published de la propriété elle-même qui indique son nom et le
champ à utiliser pour le stockage.
De telles propriétés d’interface n’ont pas besoin de méthodes d’accès. Elles
accèdent directement aux données stockées. Par convention, le champ qui stocke
la valeur de la propriété porte le même nom que la propriété, mais précédé de la
lettre F. Le champ et la propriété doivent avoir le même type.
Par exemple, la déclaration d’une propriété d’interface de type entier appelée
Year, est la suivante :
type
TMyWrapper = class(TComponent)
private
FYear: Integer; { donnée membre pour les données de la propriété Year}
published
property Year: Integer read FYear write FYear; { la propriété et son stockage }
end;
S’agissant de votre boîte A propos de, vous devez disposer de quatre propriétés
de type string pour le nom du produit, les informations de version, les
informations de copyright et les commentaires éventuels.
type
TAboutBoxDlg = class(TComponent)
private
FProductName, FVersion, FCopyright, FComments: string; { déclare les
//données membres}
published
property ProductName: string read FProductName write FProductName;
property Version: string read FVersion write FVersion;
property Copyright: string read FCopyright write FCopyright;
property Comments: string read FComments write FComments;
end;
Si vous installez votre composant dans la palette des composants et si vous le
placez dans une fiche, vous pourrez définir les propriétés, et ces valeurs
apparaîtront de façon permanente dans la fiche. Ainsi, lors de l’exécution de la
boîte de dialogue qu’il enveloppe, le composant pourra les utiliser.

43-4 Guide du développeur


Création de l’interface du composant

Ajout de la méthode Execute


Il vous reste à définir dans l’interface du composant les moyens d’ouvrir la boîte
de dialogue et de récupérer un résultat lorsqu’elle se ferme. Comme pour les
composants des boîtes de dialogue standard, vous utiliserez une fonction
booléenne appelée Execute qui renvoie True si l’utilisateur clique sur OK, ou False
s’il annule la boîte de dialogue.
La déclaration de la méthode Execute ressemble toujours à ceci :
type
TMyWrapper = class(TComponent)
public
function Execute: Boolean;
end;
L’implémentation minimale de la méthode Execute doit construire la fiche de la
boîte de dialogue, puis afficher celle-ci en tant que boîte de dialogue modale
avant de renvoyer True ou False, selon la valeur renvoyée par ShowModal.
Voici l’implémentation minimale de la méthode Execute pour une fiche boîte de
dialogue de type TMyDialogBox :
function TMyWrapper.Execute: Boolean;
begin
DialogBox := TMyDialogBox.Create(Application); { construit la fiche}
try
Result := (DialogBox.ShowModal = IDOK); { exécute; définit le résultat selon la
//façon de fermer}
finally
DialogBox.Free; { restitue la fiche}
end;
end;
Notez l’utilisation d’un bloc try..finally qui vérifie que l’application restitue
l’objet boîte de dialogue même si une exception se produit. En général, lorsque
vous construisez un objet de cette manière, vous devez utiliser un bloc
try..finally pour protéger le bloc de code et être sûr que l’application libère
toutes les ressources qu’elle alloue.
En pratique, il y a davantage de code dans le bloc try..finally. Plus
spécifiquement, avant l’appel à ShowModal, le composant enveloppe doit définir
certaines propriétés de la boîte de dialogue en fonction de ses propres propriétés
d’interface. A l’inverse, quand ShowModal rend la main, le composant enveloppe
définit certaines de ses propriétés d’interface en fonction du résultat de
l’exécution de la boîte de dialogue.
Dans le cas de la boîte A propos de, vous devez utiliser les quatre propriétés
d'interface du composant enveloppe pour définir le contenu des libellés de la
boîte de dialogue. Comme cette dernière ne renvoie aucune information à
l'application, il n'y a rien de particulier à faire après l'appel à ShowModal.

Transformation d’une boîte de dialogue en composant 43-5


Test du composant

Dans la partie publique de la classe TAboutDlg, ajoutez la déclaration pour la


méthode Execute :
type
TAboutDlg = class(TComponent)
public
function Execute: Boolean;
end;
function TAboutBoxDlg.Execute: Boolean;
begin
AboutBox := TAboutBox.Create(Application); { construit la boîte A propos de}
try
if ProductName = '' then { si le nom du produit est vide... }
ProductName := Application.Title; { ...utilise à la place le titre de
//l’application}
AboutBox.ProductName.Caption := ProductName; { copie le nom du produit}
AboutBox.Version.Caption := Version; { copie les infos de version}
AboutBox.Copyright.Caption := Copyright; { copie les infos de copyright }
AboutBox.Comments.Caption := Comments; { copie les commentaires }
AboutBox.Caption := 'About ' + ProductName; { définit le titre de la boîte}
with AboutBox do begin
ProgramIcon.Picture.Graphic := Application.Icon; { copie l’icône }
Result := (ShowModal = IDOK); { exécute et définit le résultat }
end;
finally
AboutBox.Free; { restitue la boîte de dialogue A propos de}
end;
end;

Test du composant
Une fois le composant boîte de dialogue installé, vous pouvez l’utiliser comme
n’importe quelle autre boîte de dialogue commune, en le plaçant sur une fiche et
en l’exécutant. Un moyen rapide de vérifier le fonctionnement de la boîte A
propos de consiste à ajouter un bouton de commande dans une fiche et à
exécuter la boîte de dialogue lorsque l’utilisateur clique sur ce bouton.
Par exemple, si vous avez créé une boîte de dialogue A propos de, et si vous
l’avez ajouté à la palette des composants, vous pouvez tester son fonctionnement
en suivant les étapes ci-dessous :
1 Créez un nouveau projet.
2 Placez un composant A propos de dans la fiche principale.
3 Placez un bouton de commande dans la fiche.
4 Double-cliquez sur le bouton de commande pour créer un gestionnaire
d’événements vide.
5 Dans le gestionnaire d’événements, entrez la ligne de code suivante :
AboutBoxDlg1.Execute;
6 Exécutez l’application.

43-6 Guide du développeur


Test du composant

Lorsque la fiche principale apparaît, cliquez sur le bouton de commande. La


boîte A propos de s’affiche avec l’icône projet par défaut et Project1 comme titre.
Choisissez OK pour fermer la boîte de dialogue.
Vous pouvez pousser plus loin le test du fonctionnement du composant en
définissant les différentes propriétés du composant A propos de et en exécutant
une nouvelle fois l’application.

Transformation d’une boîte de dialogue en composant 43-7


43-8 Guide du développeur
Partie

V
Développement
Part V

d’applications COM
Les chapitres de cette partie présentent les concepts nécessaires à la construction
d’applications COM : contrôleurs Automation, serveurs Automation, contrôles
ActiveX et applications MTS.
Remarque Toutes les éditions de Delphi prennent en charge les contrôleurs Automation,
mais vous avez besoin de l’édition Professionnelle ou Entreprise pour créer des
serveurs.

Développement d’applications COM


Chapitre

Présentation Chapter 44
44
des technologies COM
Delphi fournit des experts et des classes qui facilitent l’implémentation
d’applications basées sur COM (Component Object Model) de Microsoft. Grâce à
ces experts, vous pouvez créer des classes et des composants basés sur COM,
que vous utiliserez dans des applications, comme des objets COM sophistiqués,
des serveurs et des clients Automation (contrôleurs), des contrôles ActiveX, des
pages Active Server ou des fiches ActiveForms.
COM est un modèle de composant logiciel indépendant du langage, conçu par
Microsoft pour permettre l’interaction entre les composants logiciel et les
applications. Microsoft a étendu cette technologie avec ActiveX, qui est
essentiellement utilisée pour le développement Intranet.
L’aspect majeur de COM est qu’il permet la communication entre les
composants, entre les applications et entre clients et serveurs par le biais
d’interfaces clairement définies. Les interfaces représentent pour les clients un
moyen d’obtenir à l’exécution les fonctionnalités prises en charge par un
composant COM. Pour que votre composant fournisse des fonctionnalités
supplémentaires, il suffit d’ajouter une autre interface à ces fonctionnalités.
Les applications peuvent accéder aux composants COM et aux interfaces
correspondantes s’ils figurent sur le même ordinateur que les applications ou,
s’ils figurent sur un autre ordinateur du réseau, à l’aide d’un mécanisme appelé
Distributed COM ou DCOM. Pour plus d’informations sur les clients, les
serveurs et les interfaces, voir, “Composantes d’une application COM,” à la
page 44-3.
Ce chapitre présente les concepts généraux de la technologie sur laquelle
s’appuient l’Automation et les contrôles ActiveX. Les chapitres suivants traiteront
en détail de la création d’objets Automation et de contrôles ActiveX dans Delphi.

Présentation des technologies COM 44-1


COM, spécification et implémentation
COM est à la fois une spécification et une implémentation. La spécification COM
définit la création des objets et la communication entre les objets. Selon cette
spécification, les objets COM peuvent être écrits dans différents langages,
exécutés dans différents espaces de processus et sur différentes plates-formes.
Tant que les objets adhèrent à la spécification, ils peuvent communiquer. Cela
vous permet d’intégrer le code de composants existants à de nouveaux
composants implémentés dans des langages orientés objet.
L’implémentation COM est la bibliothèque COM (comprenant OLE32.dll et
OLEAut32.dll), qui fournit de nombreux services intégrés supportant la spécification
écrite. La bibliothèque COM contient un ensemble d’interfaces standard définissant
la fonctionnalité interne d’un objet COM et un petit ensemble de fonctions API pour
la création et la gestion des objets COM.
Remarque Les objets et les langages des interfaces Delphi sont conformes à la spécification
COM. L’implémentation Delphi de la spécification COM est appelée le cadre de
travail Delphi ActiveX (DAX). La plus grande partie de l’implémentation se
trouve dans l’unité AxCtrls.
Lorsque vous utilisez dans votre application les experts de Delphi et les objets
de la VCL, vous utilisez l’implémentation Delphi de la spécification COM. En
outre, Delphi fournit quelques enveloppes pour les services COM dont les
fonctionnalités ne sont pas implémentées directement, comme les documents
Active. Ces enveloppes sont définies dans l’unité ComObj et les définitions API
se trouvent dans l’unité AxCtrls.

Extensions de COM
COM a évolué et a été étendu au-delà des services de base. COM sert de
fondement à d’autres technologies, comme l’Automation, aux contrôles ActiveX,
aux pages Active Server et aux documents Active. Pour plus de détails, voir
“Extensions de COM” à la page 44-9.
En outre, vous pouvez créer un objet COM qui fonctionne dans l’environnement
MTS (Microsoft Transaction Server). MTS est un système de traitement des
transactions basé sur les composants, permettant de construire, déployer et gérer
de grosses applications serveur intranet et Internet. Bien que MTS ne fasse pas
partie de l’architecture COM, il a été conçu pour étendre les capacités de COM
dans un vaste environnement distribué. Pour plus d’informations sur MTS, voir
chapitre 50, “Création des objets MTS.”
Delphi fournit des experts permettant d’implémenter facilement des applications
qui incorporent toutes ces technologies dans l’environnement Delphi. Pour plus
de détails, voir “Implémentation des objets COM à l’aide d’experts” à la
page 44-18

44-2 Guide du développeur


Composantes d’une application COM

Composantes d’une application COM


Quand vous implémentez une application COM, vous fournissez ceci :

Interface COM Le moyen par lequel un objet expose ses services aux
clients. Un objet COM fournit une interface pour chaque
ensemble de méthodes (fonctions membre) et de propriétés
(membres de données et/ou contenu) connexes.
Serveur COM Un module, EXE, DLL ou OCX, contenant le code d’un
objet COM. Les implémentations d’objets résident sur les
serveurs. Un objet COM implémente une ou plusieurs
interfaces.
Client COM Le code appelant les interfaces afin d’obtenir du serveur les
services demandés. Les clients savent ce qu’ils veulent
obtenir du serveur (via l’interface) ; les clients ne savent pas
comment en interne le serveur fournit les services. Le client
COM le plus courant à implémenter est un contrôleur
Automation. Delphi facilite le processus de création de
client en vous permettant d’installer des serveurs COM (tel
qu’un document Word ou une diapositive Powerpoint) en
tant que composants sur la palette de composants.

Interfaces COM
Les clients COM communiquent avec des objets par le biais d’interfaces COM. Les
interfaces sont des groupes de routines, liées par la logique ou par la sémantique,
qui assurent la communication entre le fournisseur d’un service (objet serveur) et
ses clients. Voici la représentation standard d’une interface COM :
Figure 44.1 Une interface COM

Par exemple, chaque objet COM implémente l’interface de base, IUnknown, qui
indique au client les interfaces disponibles sur le client.
Les objets peuvent avoir plusieurs interfaces, où chacune implémente une
fonctionnalité. L’interface est le moyen de mettre à disposition du client le
service fourni par l’objet, sans lui donner les détails de l’implémentation sur la
façon dont ce service est fourni.
Les aspects majeurs des interfaces COM sont les suivants :
• Une fois publiées, les interfaces sont immuables ; c’est-à-dire qu’elles ne
changent plus. Une interface permet d’accéder à un ensemble précis de
fonctions. Les fonctionnalités supplémentaires sont fournies par le biais
d’interfaces supplémentaires.

Présentation des technologies COM 44-3


Composantes d’une application COM

• Par convention, les identificateurs d’interfaces COM commencent par un


I majuscule suivi d’un nom symbolique définissant l’interface, comme IMalloc
ou IPersist.
• L’identification unique des interfaces est garantie par un GUID (Globally
Unique Identifier), qui est un nombre aléatoire de 128 bits. Les GUID utilisés
pour identifier les interfaces sont appelés IID (Identificateurs d’interfaces). Ils
permettent d’éliminer les conflits de noms entre différentes versions d’un
produit ou différents produits.
• Les interfaces sont indépendantes du langage. Vous pouvez utiliser n’importe
quel langage pour implémenter une interface COM, à condition que ce
langage supporte les structures de pointeurs et puisse appeler une fonction via
un pointeur, de façon explicite ou implicite.
• Les interfaces ne sont pas elles-mêmes des objets ; elles fournissent l’accès à
un objet. Donc, les clients n’ont pas accès directement aux données ; ils
accèdent aux données par le biais d’un pointeur d’interface.
• Les interfaces sont toujours dérivées de l’interface de base, IUnknown.
• Les interfaces peuvent être redirigées par COM via des proxy pour permettre
aux appels de méthodes de l’interface de s’effectuer entre différents threads,
processus et machines en réseau, sans que les objets client ou serveur ne
soient jamais informés de la redirection. Pour plus d’informations, voir,
“Serveurs en processus, hors processus et distants,” à la page 44-6.

L’interface COM de base, IUnknown


Les objets COM doivent tous supporter l’interface de base, appelée IUnknown,
qui contient les routines suivantes :

QueryInterface Fournit des pointeurs sur d’autres interfaces supportées par


l’objet.
AddRef et Release Méthodes simples de décompte de références qui permettent à
un objet de contrôler sa durée de vie et de se supprimer lui-
même lorsque le client n’a plus besoin de son service.

Les clients obtiennent des pointeurs sur d’autres interfaces via la méthode
QueryInterface de IUnknown. QueryInterface connaît chaque interface de l’objet
serveur et peut donner au client un pointeur vers l’interface demandée. Lorsqu’il
reçoit un pointeur vers une interface, le client est assuré de pouvoir appeler
n’importe quelle méthode de l’interface.
Les objets contrôlent leur propre durée de vie grâce aux méthodes AddRef et
Release de IUnknown, qui sont de simples méthodes de décompte de références.
Tant que le décompte de références est différent de zéro, l’objet reste en
mémoire. Dès qu’il atteint zéro, l’implémentation de l’interface peut en toute
sécurité disposer du ou des objets sous-jacents.

44-4 Guide du développeur


Composantes d’une application COM

Pointeurs d’interface COM


Un pointeur d’interface est un pointeur de 32 bits vers une instance d’objet qui
pointe, à son tour, vers l’implémentation de chaque méthode de l’interface.
L’implémentation est accédée via un tableau de pointeurs vers ces méthodes,
appelé vtable. Les vtables sont similaires au mécanisme utilisé pour gérer les
fonctions virtuelles sous Pascal Objet.
La vtable est partagée par toutes les instances d’une classe objet, et pour chaque
instance de l’objet, le code de l’objet alloue une deuxième structure contenant ses
données privées. Le pointeur d’interface du client, est alors un pointeur vers le
pointeur vers la vtable, comme le montre le diagramme suivant.
Figure 44.2 Vtable d’interface

Serveurs COM
Un serveur COM est une application ou une bibliothèque qui fournit des
services à une application ou bibliothèque client. Un serveur COM est constitué
d’un ou de plusieurs objets COM, un objet COM étant un ensemble de
propriétés (données membre ou contenu) et de méthodes (fonctions membre).
Les clients ne savent pas comment l’objet COM effectue son service ;
l’implémentation de l’objet est encapsulée. Un objet met ses services à disposition
par le biais de ses interfaces comme décrit précédemment.
En outre, les clients n’ont pas besoin de savoir où réside l’objet COM. COM
fournit un accès transparent quel que soit l’emplacement de l’objet.
Quand il demande un service à un objet COM, le client transmet un
identificateur de classe (CLSID) à COM. Un CLSID est juste un GUID qui
référence un objet COM. COM utilise ce CLSID pour localiser l’implémentation
appropriée du serveur, amène le code en mémoire, et fait créer par le serveur
une instance de l’objet pour le client. Un serveur COM doit donc fournir un
objet fabricant de classe (IClassFactory) qui crée sur demande des instances
d’objet. Le CLSID est basé sur le GUID de l’interface.
En général, un serveur COM doit effectuer ceci :
• Recenser des entrées dans le registre système pour associer le module serveur
à l’identificateur de classe (CLSID).

Présentation des technologies COM 44-5


Composantes d’une application COM

• Implémenter un objet fabricant de classe, qui est un type d’objet spécial qui
fabrique un autre objet d’un CLSID particulier.
• Exposer le fabricant d’objet à COM.
• Fournir un mécanisme de déchargement grâce auquel un serveur qui ne sert
pas de client pourra être supprimé de la mémoire.
Remarque Les experts de Delphi automatisent la création des objets et des serveurs COM
comme décrit dans “Implémentation des objets COM à l’aide d’experts” à la
page 44-18.

CoClasses et fabricants de classes


Un objet COM est une instance d’une CoClasse, qui est une classe implémentant
une ou plusieurs interfaces COM. L’objet COM fournit les services définis par
ses interfaces des CoClasses.
Les CoClasses sont instanciées par un type d’objet spécial, appelé un fabricant de
classe. Chaque fois que des services d’un objet sont demandés par un client, un
fabricant de classe crée et recense une instance de cet objet pour ce client
particulier. Si un autre client demande les services de l’objet, le fabricant de
classe crée une autre instance de l’objet pour ce deuxième client.
Une CoClasse doit posséder un fabricant de classe et un identificateur de classe
(CLSID) de sorte que son objet COM puisse être instancié en externe, c’est-à-dire
pour un autre module. L’utilisation de ces identificateurs uniques pour les
CoClasses implique qu’elles peuvent être mises à jour chaque fois que de
nouvelles interfaces sont implémentées dans leur classe. Une nouvelle interface
peut modifier ou ajouter des méthodes sans affecter les versions antérieures, ce
qui est un problème courant lorsqu’on utilise des DLL.
Les experts de Delphi prennent en compte l’implémentation et l’instanciation des
fabricants de classe.

Serveurs en processus, hors processus et distants


Avec COM, un client n’a pas besoin de savoir où réside un objet, il suffit de
faire un appel à une interface de l’objet. COM accomplit les étapes nécessaires à
cet appel. Ces étapes sont différentes selon que l’objet réside dans le même
processus que le client, dans un autre processus sur la machine du client ou sur
une autre machine du réseau. Ces différents types de serveurs sont décrits ici :
Serveur en Une bibliothèque (DLL) s’exécutant dans le même espace processus
processus que le client, par exemple, un contrôle ActiveX incorporé dans
une page Web visualisée sous Internet Explorer ou Netscape. Le
contrôle ActiveX est alors téléchargé sur la machine du client et
appelé dans le même processus que le navigateur Web.
Le client communique avec le serveur en processus grâce à des
appels directs à l’interface COM.

44-6 Guide du développeur


Composantes d’une application COM

Serveur hors Une autre application (EXE) s’exécutant dans un espace processus
processus (ou différent mais sur la même machine que le client. Par exemple, une
serveur local) feuille de calcul Excel incorporée dans un document Word
constitue deux applications distinctes tournant sur la même
machine.
Le serveur local utilise COM pour communiquer avec le client.
Serveur distant Une DLL ou une autre application s’exécutant sur une machine
différente de celle du client. Par exemple, une application Delphi
de base de données connectée à un serveur d’application sur une
autre machine du réseau.
Le serveur distant utilise des interfaces COM distribuées (DCOM)
pour communiquer avec le serveur d’application.

Comme illustré dans la figure suivante, pour les serveurs en processus, les
pointeurs sur les interfaces de l’objet sont dans le même espace processus que le
client, et COM fait des appels directs dans l’implémentation de l’objet.
Figure 44.3 Serveurs en processus

Comme illustré dans la figure suivante, quand le processus est soit différent, soit
sur une autre machine, COM utilise un proxy pour initier les appels de
procédure distants. Le proxy réside dans le même processus que le client, de
sorte que vu du client, tous les appels à des interfaces semblent pareils. Le proxy
intercepte l’appel du client et le transmet là où l’objet réel s’exécute. Le
mécanisme qui permet aux clients d’accéder aux objets d’un espace processus
différent, ou même d’une machine différente, comme s’ils se trouvaient dans leur
propre processus, est appelé le marshaling.
La différence entre les serveurs hors processus et distants est le type de
communication inter-processus utilisé. Le proxy utilise COM pour communiquer
avec un serveur hors processus et COM distribué (DCOM) pour communiquer
avec une machine distante.

Présentation des technologies COM 44-7


Composantes d’une application COM

Figure 44.4 Serveurs hors processus et distants

Le mécanisme du marshaling
Le marshaling est le mécanisme qui permet à un client de faire des appels aux
fonctions de l’interface d’objets distants qui se trouvent dans un autre processus
ou sur une autre machine. Le marshaling
• Prend un pointeur d’interface dans le processus du serveur et rend un
pointeur de proxy disponible au code dans le processus du client.
• Prend les arguments d’un appel à l’interface passés depuis le client et les
place dans l’espace processus de l’objet distant.
Pour tout appel à l’interface, le client met les arguments sur une pile et émet un
appel à une fonction via le pointeur d’interface. Si l’appel à l’objet n’est pas en
processus, il est passé au proxy. Celui-ci compresse les arguments dans un
paquet de marshaling et transmet la structure à l’objet distant. Le stub de l’objet
décompresse le paquet, place les arguments sur la pile et appelle
l’implémentation de l’objet. L’objet recrée l’appel du client dans son propre
espace d’adressage.
Le type de marshaling dépend de ce que l’objet COM implémente. Les objets
peuvent utiliser le mécanisme de marshaling standard fourni par l’interface
IDispatch. C’est un mécanisme de marshaling générique qui permet la
communication via un appel standard à une procédure distante (RPC). Pour plus
de détails sur l’interface IDispatch, voir chapitre 47, “Création d’un serveur
Automation.”
Remarque MTS (Microsoft Transaction Server) apporte un support supplémentaire pour les
objets distants. Pour plus de détails, voir chapitre 50, “Création des objets MTS”.

44-8 Guide du développeur


Extensions de COM

Clients COM
Il est important de concevoir une application COM dans laquelle les clients
peuvent interroger les interfaces d’un objet pour déterminer ce que celui-ci peut
offrir. Les objets serveur ne sont pas concernés par l’utilisation qu’en fait le
client. Celui-ci connaît ce qu’un objet peut fournir via ses interfaces. En outre, les
clients n’ont pas besoin de savoir comment (ou même où) un objet fournit ses
services ; c’est le problème de l’objet de fournir le service publié par le biais de
l’interface.
Un client COM typique est le contrôleur Automation. Le contrôleur Automation
est la partie de l’application qui a la vue la plus complète des objectifs de
l’application. Il connaît le type d’information dont elle a besoin des divers objets
du serveur et il demande les services lorsque c’est nécessaire.
Delphi facilite le développement d’un contrôleur Automation en permettant
d’importer la bibliothèque de types d’un serveur Automation et de l’installer sur la
palette de composants.
Pour plus de détails sur la création d’un contrôleur Automation, voir chapitre 46,
“Création d’un contrôleur Automation”.

Extensions de COM
COM a été initialement conçu pour fournir une fonctionnalité de communication
de base et permettre l’enrichissement de cette fonctionnalité via des extensions.
COM lui-même a étendu sa fonctionnalité première en définissant des ensembles
spécialisés d’interfaces couvrant des besoins spécifiques.
ActiveX est une technologie qui permet aux composants COM, particulièrement
les contrôles, d’être plus compacts et plus efficaces. Cela est notamment
nécessaire pour les contrôles destinés aux applications Intranet qui doivent être
téléchargées par un client pour pouvoir être utilisées.
Microsoft incorpore au modèle COM certaines des technologies MTS qui
permettront de construire de complexes applications Internet et intranet. La
phase suivante de COM, qui incorpore aussi de nouvelles fonctionnalités, est
actuellement appelée “COM+” (COM Plus) et devrait être disponible en même
temps que Windows 2000.

Présentation des technologies COM 44-9


Extensions de COM

Le tableau suivant est un résumé des extensions de services que COM fournit
actuellement. Les sections suivantes décrivent ces services en détail.

Serveurs L’Automation désigne la capacité d’une application à


Automation contrôler par programme les objets d’une autre application.
Les serveurs Automation sont les objets qui peuvent être
contrôlés par d’autres exécutables à l’exécution.
Contrôleurs Clients des serveurs Automation. Les contrôleurs fournissent
Automation (ou un environnement de programmation dans lequel le
clients COM) développeur ou l’utilisateur peut écrire des scripts (contrôles)
pour diriger les serveurs Automation.
Contrôles ActiveX Les contrôles ActiveX sont des serveurs en processus COM,
généralement conçus pour être incorporés dans une
application client. Les contrôles proposent des
comportements et des événements à la conception et à
l’exécution.
Bibliothèques Collection de structures de données statiques, souvent
de types enregistrées en tant que ressource, fournissant des
informations de type détaillées sur un objet et ses interfaces.
Les clients des serveurs Automation et les contrôles ActiveX
ont besoin des informations de type.
Pages Active Server Les pages Active Server sont des composants ActiveX qui
permettent de créer des pages Web dynamiques.
Documents Active Objets supportant la liaison et l’incorporation, le glisser-
déplacer, l’édition visuelle et l’activation in-situ. Les
documents Word et les feuilles de calcul Excel sont des
exemples de documents Active.
Objets visuels Objets pouvant être manipulés entre différents processus.
inter-processus

Le diagramme page suivante montre les relations entre les extensions de COM et
la façon dont elles dérivent de COM.

44-10 Guide du développeur


Extensions de COM

Figure 44.5 Technologies COM

L’utilisation d’objets COM amène à la fois des fonctionnalités et des restrictions.


Ces objets peuvent être visuels ou non visuels. Certains s’exécutent dans le même
espace processus que leurs clients ; d’autres peuvent s’exécuter dans des processus
différents ou sur des machines distantes si les objets assurent le marshaling.
Le tableau suivant récapitule les types d’objets COM que vous pouvez créer, s’ils
sont visuels, les espaces processus dans lesquels ils peuvent s’exécuter, le
marshaling qu’ils fournissent et s’ils ont besoin d’une bibliothèque de types.
Tableau 44.1 Exigences des objets COM
Objet Espace Bibliothèque
Objet visuel ? processus Communication de types
Document Active Habituellement En processus ou Verbes OLE Non
hors processus
Automation Parfois En processus, Marshaling automatique Requise pour le
hors processus via l’interface IDispatch marshaling
ou distant (pour les serveurs hors automatique
processus ou distants)
Contrôle ActiveX Habituellement En processus Marshaling automatique Requise
via l’interface IDispatch
Objet interface Optionellement En processus Pas de marshaling requis Recommandée
personnalisé pour les serveurs en
processus
Objet interface Optionellement En processus, Marshaling automatique Recommandée
personnalisé hors processus via une bibliothèque de
ou distant types ; sinon, marshaling
manuel via des interfaces
personnalisées

Présentation des technologies COM 44-11


Extensions de COM

Serveurs et contrôleurs Automation


L’Automation désigne la capacité d’une application à contrôler par programme
les objets d’une autre application, comme une macro qui peut manipuler
plusieurs applications à la fois. Le client d’un objet Automation est appelé
contrôleur Automation, et l’objet serveur manipulé est appelé objet Automation.
L’Automation peut être utilisée sur des serveurs en processus, locaux ou
distants.
L’Automation présente deux caractéristiques :
• L’objet Automation doit être capable de définir un ensemble de propriétés et
de commandes, et de décrire ses capacités via les descriptions de type. Pour
ce faire, il doit disposer d’un moyen de fournir des informations sur les
interfaces de l’objet, les méthodes des interfaces et les arguments de ces
méthodes. Généralement, ces informations se trouvent dans des bibliothèques
de types. Le serveur Automation peut aussi générer des informations
dynamiquement quand elles lui sont demandées.
• Les objets Automation doivent rendre ces méthodes accessibles pour que
d’autres applications puissent les utiliser. Pour cela, ils doivent implémenter
l’interface IDispatch. C’est par le biais de cette interface qu’un objet expose
toutes ses méthodes et propriétés. Et c’est par le biais de la méthode primaire
de cette interface que les méthodes de l’objet peuvent être appelées, une fois
qu’elles ont été identifiées grâce aux informations de type.
Les développeurs utilisent souvent l’Automation pour créer et utiliser des objets
OLE non visuels qui s’exécutent dans n’importe quel espace processus, car
l’interface Automation IDispatch automatise le processus de marshaling. En
revanche, l’Automation limite les types que vous pouvez utiliser.
Pour obtenir une liste de types compatibles avec les bibliothèques de types en
général, et d’interfaces Automation en particulier, voir la section Types valides dans
le chapitre 49, “Utilisation des bibliothèques de types.”
Pour plus de détails sur l’écriture d’un contrôleur Automation, voir le
chapitre 46, “Création d’un contrôleur Automation”. Pour plus d’informations sur
l’écriture d’un serveur Automation, voir le chapitre 47, “Création d’un serveur
Automation”.

Contrôles ActiveX
Les contrôles ActiveX sont des contrôles visuels qui s’exécutent seulement dans
des serveurs en processus, et peuvent être incorporés dans une application
conteneur de contrôles ActiveX. Ce ne sont pas eux-mêmes des applications
complètes, mais de simples contrôles OLE préfabriqués réutilisables dans
diverses applications. Les contrôles ActiveX utilisent l’Automation pour exposer
leurs propriétés, méthodes et événements. Leurs fonctionnalités incluent la
capacité à déclencher des événements, la liaison aux sources de données et la
gestion de licence.

44-12 Guide du développeur


Extensions de COM

De plus en plus fréquemment, les contrôles ActiveX s’utilisent dans un site Web
comme objets interactifs placés dans une page Web. Ainsi, ActiveX est devenu
un standard particulièrement destiné à des contenus interactifs pour le Web, y
compris l’utilisation de documents ActiveX employés pour visualiser des
documents non HTML via un navigateur Web. Pour plus d’informations sur la
technologie ActiveX, voir le site Web de Microsoft.
Les experts de Delphi facilitent la création des contrôles ActiveX. Pour plus
d’informations sur la création et l’utilisation de ces types d’objets, voir
chapitre 48, “Création d’un contrôle ActiveX”.

Bibliothèques de types
Les bibliothèques de types offrent un moyen d’obtenir davantage d’informations
de type sur un objet que les interfaces de l’objet. Les bibliothèques de types
contiennent les informations nécessaires sur les objets et leurs interfaces, comme
les interfaces associées à tels objets (étant donné le CLSID), les fonctions membre
de chaque interface et les arguments requis par ces fonctions.
Vous pouvez obtenir les informations de type en interrogeant une instance d’un
objet pendant qu’elle s’exécute ou, en chargeant et en lisant les bibliothèques de
types. Grâce à ces informations, vous pouvez implémenter un client qui utilise
un objet souhaité, en sachant exactement les fonctions membre dont vous avez
besoin, et ce qu’il faut passer à ces fonctions.
Les clients des serveurs Automation et des contrôles ActiveX s’attendent à
trouver ces informations de type. Les experts Automation et ActiveX génèrent
automatiquement une bibliothèque de types. Vous pouvez voir ou modifier ces
informations de type en utilisant l’éditeur de bibliothèques de types comme
décrit dans le chapitre 49, “Utilisation des bibliothèques de types”.
Cette section décrit le contenu d’une bibliothèque de types, comment la créer,
quand l’utiliser et comment y accéder. Pour les développeurs souhaitant partager
des interfaces à travers divers langages, la section se termine par des suggestions
sur l’utilisation des outils de gestion de bibliothèques de types.

Contenu d’une bibliothèque de types


Les bibliothèques de types contiennent des informations de type qui indiquent
quelles interfaces existent et dans quels objets COM, ainsi que le type et le
nombre d’arguments des méthodes d’interface. Ces descriptions incluent les
identificateurs uniques de CoClasses (CLSID) et d’interfaces (IID), pour que
l’utilisateur y accède de façon correcte, ainsi que les identificateurs de répartition
(dispID) pour les méthodes et propriétés d’interface Automation.
Les bibliothèques de types peuvent aussi contenir les informations suivantes :
• une description des informations personnalisées de type associées aux
interfaces personnalisées
• des routines exportées par le serveur Automation ou ActiveX mais qui ne sont
pas des méthodes d’interface

Présentation des technologies COM 44-13


Extensions de COM

• des informations concernant l’énumération, les enregistrements (structures), les


unions, les alias et les types des données des modules
• des références aux descriptions de types issues d’autres bibliothèques de types

Création de bibliothèques de types


Avec les outils de développement traditionnels, vous créez des bibliothèques de
types en écrivant des scripts en IDL (Interface Definition Language) ou en ODL
(Object Description Language), puis en compilant ces scripts. En revanche,
Delphi génère automatiquement une bibliothèque de types lorsque vous utilisez
l’expert serveur Automation ou contrôle ActiveX. Vous pouvez également créer
une bibliothèque de types en choisissant dans le menu principal Fichier|
Nouveau|ActiveX|Bibliothèque de types. Vous pouvez ensuite voir la
bibliothèque de types en utilisant l’éditeur de bibliothèques de types Delphi. Il
est facile de modifier la bibliothèque de types à l’aide de l’éditeur de
bibliothèques et Delphi met automatiquement à jour les fichiers source
appropriés.
L’éditeur de bibliothèques de types génère automatiquement une bibliothèque de
types standard, généralement en tant que ressource, en même temps qu’un
fichier interface Delphi (fichier .PAS) contenant la définition de l’interface dans la
syntaxe Pascal Objet. Pour plus d’informations sur l’utilisation de l’éditeur de
bibliothèques de types pour écrire des interfaces et des CoClasses, voir
chapitre 49, “Utilisation des bibliothèques de types”.

Quand utiliser les bibliothèques de types


Il est important de créer une bibliothèque de types pour chaque ensemble
d’objets qui est présenté aux utilisateurs finaux, par exemple,
• Les contrôles ActiveX nécessitent une bibliothèque de types, qui doit être
incluse en tant que ressource dans la DLL qui contient les contrôles ActiveX.
• Les objets présentés qui gèrent la liaison de vtable (“vtable binding”) des
interfaces personnalisées doivent être décrites dans une bibliothèque de types
car les références à la vtable sont liées lors de la compilation. Pour plus de
détails sur les vtables et les liaisons effectuées lors de la compilation, voir
“Création d’un objet Automation pour une application” à la page 47-1.
• Les applications qui implémentent des serveurs Automation doivent fournir
une bibliothèque de types pour que les clients puissent faire une liaison
immédiate.
• Les objets instanciés depuis des classes qui gèrent l’interface IProvideClassInfo
tels que tous les descendants de la classe VCL TTypedComObject, doivent
posséder une bibliothèque de types.
• Les bibliothèques de types ne sont pas nécessaires mais utiles pour identifier
les objets OLE utilisables par glisser-déplacer.
Si vous définissez des interfaces à usage exclusivement interne (au sein d’une
application), il n’est pas nécessaire de créer une bibliothèque de types.

44-14 Guide du développeur


Extensions de COM

Accès aux bibliothèques de types


En règle générale, une bibliothèque de types fait partie d’un fichier de ressource
(.RES) ou d’un fichier autonome à l’extension .TLB. Lorsqu’une bibliothèque de
types a été créée, les scruteurs d’objets, les compilateurs et les outils similaires
peuvent y accéder par des interfaces spéciales :

Interface Description
ITypeLib Fournit des méthodes pour accéder à la description d’une bibliothèque
de types.
ITypeInfo Fournit la description de chaque objet d’une bibliothèque de types. Par
exemple, un navigateur utilise cette interface pour extraire des
informations sur les objets de la bibliothèque de types.
ITypeComp Fournit un moyen rapide d’accéder aux informations dont le
compilateur a besoin lors de la liaison avec une interface.

Delphi peut importer et utiliser des bibliothèques de types venant d’autres


applications. La plupart des classes de la VCL employées pour les applications
COM supportent les interfaces essentielles utilisées pour stocker et récupérer les
informations de types à partir des bibliothèques de types et des instances actives
d’un objet. La classe TTypedComObject de la VCL supporte les interfaces qui
fournissent des informations de type et s’utilise comme une fondation pour
l’environnement objet ActiveX.

Avantages des bibliothèques de types


Même si votre application ne nécessite pas de bibliothèque de types, considérez
les avantages suivants :
• La vérification des types peut se faire lors de la compilation.
• Vous pouvez utiliser la liaison immédiate avec l’automation (ce qui remplace
les appels par le biais de variants) et les contrôleurs qui ne gèrent pas les
vtables ou les interfaces doubles peuvent coder les dispID lors de la
compilation pour améliorer les performances de l’application.
• Les scruteurs de types peuvent parcourir la bibliothèque. Les clients pourront
donc voir les caractéristiques de vos objets.
• La fonction RegisterTypeLib peut être utilisée pour recenser vos objets présentés
dans la base de données de recensement.
• La fonction UnRegisterTypeLib peut être utilisée pour désinstaller complètement
la bibliothèque de types d’une application du registre.
• L’accès local au serveur est accéléré car l’automation utilise les informations
de la bibliothèque de types pour regrouper les paramètres qui sont passés à
un objet d’un autre processus.

Présentation des technologies COM 44-15


Extensions de COM

Utilisation des outils de bibliothèques de types


Les outils permettant de manipuler des bibliothèques de types sont indiqués
ci-dessous .
• L’outil TLIBIMP (importation de bibliothèque de types), qui crée des fichiers
d’interface Delphi à partir de bibliothèques de types est intégré dans l’éditeur
de bibliothèques de types. TLIBIMP offre des options de configuration
supplémentaires non disponibles dans l’éditeur de bibliothèques de types.
• Le compilateur Microsoft IDL (MIDL) compile les scripts IDL pour créer une
bibliothèque de types.
• MKTYPLIB est un compilateur ODL qui compile les scripts ODL pour créer
une bibliothèque de types, qui se trouve dans le SDK Win32 de Microsoft.
• OLEView est un outil de visualisation de bibliothèque de types disponible sur
le site Web de Microsoft.
• TRegSvr est un outil pour recenser et dérecenser les serveurs et les
bibliothèques de types, fourni avec Delphi. Le source de TRegSvr est
disponible sous la forme d’un exemple dans le répertoire Examples.
• RegSvr32.exe est un utilitaire standard de Windows pour recenser et
dérecenser les serveurs et les bibliothèques de types.

Pages Active Server


La technologie ASP (pages Active Server) vous permet de bâtir des pages Web
dynamiquement à l'aide de composants serveur ActiveX. Grâce aux pages Active
Server, vous pouvez incorporer des contrôles ActiveX dans une page Web qui
sont appelés chaque fois que le serveur charge la page Web. Par exemple, vous
pouvez écrire un programme Pascal Object, par exemple pour créer une image
bitmap ou vous connecter à une base de données, dans lequel les contrôles
accèdent aux données qui sont mises à jour chaque fois que le serveur charge la
page Web.
Les pages Active Server reposent sur l’environnement Microsoft Internet
Information Server (IIS) pour fournir vos pages Web.
Côté serveur, les pages Active Server sont des composants ActiveX que vous
pouvez développer à l'aide de Delphi ou de la plupart des langages, comme
C++, Java ou Visual Basic. Côté client, la technologie ASP repose sur un
document HTML standard visualisable par les utilisateurs sur n'importe quelle
plate-forme dotée d'un navigateur Web.
Les experts Delphi facilitent la création de pages Active Server. Pour plus
d’informations sur la création et l’utilisation de ces types d’objets, voir
chapitre 51, “Création d’une page Active Server.”

44-16 Guide du développeur


Extensions de COM

Documents Active
Les documents Active (appelés auparavant documents OLE) sont un ensemble de
services COM supportant la liaison et l’incorporation, le glisser-déplacer et
l’édition visuelle. Les documents Active intègrent de façon transparente des
données ou des objets de différents formats, par exemple des clips sonores, des
feuilles de calcul, du texte et des images.
Contrairement aux contrôles ActiveX, les documents Active ne sont pas limités
aux serveurs en processus ; ils peuvent être utilisés dans des applications inter-
processus.
A la différence des objets Automation, qui ne sont presque jamais visuels, les
objets document Active peuvent être visuellement actifs dans une autre
application. Ils sont associés à deux types de données : les données de
représentation utilisées pour l’affichage visuel à l’écran ou sur un périphérique
de sortie et les données natives utilisées pour modifier l’objet.
Les objets document Active peuvent être des conteneurs ou des serveurs de
documents. Bien que Delphi ne fournisse pas d’expert pour créer
automatiquement des documents Active, vous pouvez utiliser la classe
TOleContainer de la VCL pour supporter la liaison et l’incorporation dans les
documents Active existants.
Vous pouvez aussi utiliser TOleContainer comme base d’un conteneur de
document Active. Pour créer des objets pour les serveurs de documents Active,
utilisez une des classes de base COM de la VCL et implémentez les interfaces
appropriées à ce type d’objet, en fonction des services que l’objet doit gérer. Pour
plus d’informations sur la création et l’utilisation de serveurs de documents
Active, voir le site Web Microsoft.
Remarque Bien que la spécification des documents Active contienne une gestion intégrée du
marshaling des applications à processus croisé, les documents Active ne
s’exécutent pas sur des serveurs distants car les types qu’ils utilisent (handles de
fenêtre, de menu, etc.) sont spécifiques à un système sur une machine donnée.

Objets visuels inter-processus


Les objets Automation, les documents Active et les contrôles ActiveX sont des
objets utilisés fréquemment. Il est moins fréquent de rencontrer des objets OLE
ou ActiveX affichés visuellement et manipulés dans une application inter-
processus. Ces types d’objets sont plus délicats à créer car le protocole de
communication à utiliser pour la manipulation visuelle des objets dans des
applications inter-processus n’est standardisé que pour les objets visuels utilisant
les interfaces document Active. Vous devrez donc écrire vous-même les
interfaces implémentées par votre objet, puis gérer seul les interfaces de
marshaling.

Présentation des technologies COM 44-17


Implémentation des objets COM à l’aide d’experts

Cela peut se faire de deux façons :


• En utilisant l’interface double IDispatch, qui fournit le marshaling automatique.
Cette méthode est recommandée. C’est la plus facile et l’expert Automation
crée des interfaces doubles par défaut lorsque vous créez un objet
Automation.
• En écrivant vous-même les classes de marshaling par l’implémentation de
IMarshal ou d’interfaces connexes.

Implémentation des objets COM à l’aide d’experts


Delphi facilite l’écriture d’applications COM en fournissant des experts qui
facilitent la création des applications Delphi qui s’exécutent dans l’environnement
COM. Des experts distincts permettent de créer :
• Un simple objet COM
• Un objet Automation
• Un contrôle ActiveX
• Une page Active Server
• Une fiche ActiveX
• Une bibliothèque ActiveX
• Une page de propriétés
• Une bibliothèque de types
• Un objet MTS
Les experts automatisent les tâches intervenant dans la création de chaque type
d’objet. Ils fournissent les interfaces COM requises pour chaque type d’objet.
Comme le montre la figure 44.6, avec un simple objet COM, l’expert implémente
la seule interface COM obligatoire, IUnknown, qui fournit un pointeur d’interface
vers l’objet.
Figure 44.6 Interface d’un objet COM simple

Comme le montre la figure 44.7, pour les objets Automation, l’expert implémente
IUnknown et IDispatch, qui fournissent le marshaling automatique.
Figure 44.7 Interfaces d’un objet Automation

Comme le montre la figure 44.8, pour les objets contrôles ActiveX, l’expert
implémente toutes les interfaces requises par les contrôles ActiveX : IUnknown,
IDispatch, IOleObject, IOLEControl, etc. Pour obtenir la liste complète des
interfaces, reportez-vous à la page de référence de l’objet TActiveXControl.

44-18 Guide du développeur


Implémentation des objets COM à l’aide d’experts

Figure 44.8 Interfaces d’un contrôle ActiveX

Les experts fournissent en fait un meilleur niveau d’implémentation. Comme le


montre le tableau 44.2, les divers experts implémentent les interfaces COM
suivantes :
Tableau 44.2 Experts Delphi pour l’implémentation des objets COM, Automation et
ActiveX
Interfaces
Expert implémentées Ce que fait l’expert
Serveur COM IUnknown Exporte les routines nécessaires pour gérer le
recensement du serveur, le recensement des
classes, le chargement et le déchargement du
serveur et l’instanciation des objets.
Crée et gère les fabricants de classes pour les
objets implémentés sur le serveur.
Indique à COM d’appeler les interfaces des
objets basées sur un modèle de thread spécifié.
Fournit une bibliothèque de types, si elle est
demandée.
Serveur IUnkown, IDispatch Effectue les actions d’un expert serveur COM
Automation (décrit plus haut) plus :
Implémente l’interface que vous spécifiez,
double ou de répartition.
Fournit automatiquement une bibliothèque de
types.
Contrôle ActiveX IUnknown, IDispatch, Effectue les actions des experts serveur COM
IPersistStreamInit, et serveur Automation (décrits plus haut) plus :
IOleInPlaceActiveObject, Implémente les propriétés, méthodes et
IPersistStorage, événements pour toutes interfaces de
IViewObject, IOleObject, TActiveXControl.
IViewObject2,
IOleControl, Vous laisse dans l’éditeur de code source pour
IPerPropertyBrowsing, que vous puissiez modifier l’objet.
IOleInPlaceObject,
ISpecifyPropertyPages

Présentation des technologies COM 44-19


Implémentation des objets COM à l’aide d’experts

Tableau 44.2 Experts Delphi pour l’implémentation des objets COM, Automation et
ActiveX (suite)
Interfaces
Expert implémentées Ce que fait l’expert
ActiveForm Mêmes interfaces que Effectue les actions de l’expert contrôle
contrôle ActiveX ActiveX, plus :
Implémente les propriétés, méthodes et
événements pour toutes les interfaces de
TActiveXControl.
Vous laisse avec une fiche pour que vous
puissiez concevoir une application.
Objet Active Server IUnknown, IDispatch Effectue les actions d’un expert objet
Automation (décrit plus haut) et génère une
page .ASP qui peut être chargée dans un
navigateur Web. Vous laisse dans l’éditeur de
bibliothèques de types pour que vous puissiez
modifier les propriétés et les méthodes de
l’objet, si nécessaire.
Si vous affectez aux méthodes d’événement de
niveau page le type Active Server, ce dernier
implémente OnStartPage et OnEndPage
automatiquement.
Bibliothèque Aucune, par défaut Crée une nouvelle DLL serveur ActiveX ou
ActiveX COM et expose les fonctions d’exportation
nécessaires.
Page de propriétés IUnknown, Crée une nouvelle page de propriétés que vous
IPropertyPage pouvez concevoir dans le concepteur de fiche.
Bibliothèque Aucune, par défaut Crée une nouvelle bibliothèque de types et
de types l’associe au projet actif.
objet MTS Les méthodes Ajoute une nouvelle unité au projet en cours
d’interface contenant la définition de l’objet MTS, afin que
IObjectControl ainsi que les clients puissent accéder à ce serveur dans
Activate, Deactivate, et l’environnement d’exécution MTS. Vous laisse
CanBePooled. dans l’éditeur de bibliothèque de types pour
que vous puissiez modifier les propriétés et les
méthodes de l’objet, si nécessaire.

Si vous voulez, vous pouvez ajouter d’autres objets COM (ou refaire une
implémentation existante). Pour fournir une nouvelle interface, créez un
descendant de l’interface IDispatch et implémentez les méthodes nécessaires. Pour
implémenter à nouveau une interface, créez un descendant de cette interface et
modifiez ce descendant.

44-20 Guide du développeur


Chapitre

Création d’un objet COM simple


Chapter 45
45
Delphi met à votre disposition des experts pour vous aider à créer divers objets
COM. Ce chapitre donne une vue générale de la façon de créer un objet COM,
simple et trivial, comme une extension du shell, dans l’environnement Delphi.
Pour créer des objets Automation client et serveur, voir chapitre 46, “Création
d’un contrôleur Automation,” et 47, “Création d’un serveur Automation.” Pour
créer un contrôle ActiveX, voir chapitre 48, “Création d’un contrôle ActiveX”.
Pour créer une page de serveur Active, voir chapitre 51, “Création d’une page
Active Server.”
Le but de ce chapitre n’est pas de fournir tous les détails nécessaires à l’écriture
des applications COM. Pour ceux-ci, reportez-vous à la documentation
Developer’s Network (MSDN) de Microsoft. Le site Web de Microsoft fournit
également les informations les plus récentes concernant ce sujet.

Présentation de la création d’un objet COM


Utilisez l’expert objet COM pour créer un objet COM simple et trivial, par
exemple, une extension du shell. Un objet peut être implémenté comme serveur
en processus, serveur hors processus ou serveur distant.
L’expert objet COM réalise les tâches suivantes :
• Création d’une nouvelle unité.
• Définition d’une nouvelle classe descendant de TCOMObject et du constructeur
du fabricant de la classe.
Le processus de création d’un objet COM comprend les étapes suivantes :
1 Conception de l’objet COM.
2 Utilisation de l’expert objet COM pour créer un objet COM.
3 Recensement de l’objet COM.
4 Test de l’objet COM..

Création d’un objet COM simple 45-1


Conception d’un objet COM

Conception d’un objet COM


Lors de la conception de l’objet COM, vous devez décider quelles interfaces
COM vous souhaitez implémenter. L’expert propose l’interface IUnknown. Si
vous souhaitez implémenter d’autres interfaces COM, reportez-vous à la
documentation MSDN.
Vous devez décider si l’objet COM est un serveur en processus, un serveur hors
processus ou un serveur distant. Pour les serveurs en processus, ainsi que pour
les serveurs hors processus et les serveurs distants utilisant une bibliothèque de
types, COM effectue le marshaling des données à votre place. Dans les autres
cas, c’est à vous d’effectuer le marshaling des données destinées aux serveurs
hors processus.
Pour avoir des informations sur les types de serveurs, voir “Serveurs en
processus, hors processus et distants” à la page 44-6.

Création d’un objet COM avec l’expert objet COM


Avant de créer un objet COM, créez un projet, ou ouvrez-en un, pour
l’application contenant la fonction que vous souhaitez implémenter. Le projet
peut être une application ou une bibliothèque ActiveX, en fonction de vos
besoins.
Pour faire apparaître l’expert objet COM,
1 Choisissez Fichier|Nouveau pour ouvrir la boîte de dialogue Nouveaux
éléments.
2 Sélectionnez l’onglet intitulé ActiveX.
3 Double-cliquez sur l’icône des objets COM.
Dans l’expert, spécifiez ce qui suit :

Nom de la classe Spécifiez le nom de l’objet que vous souhaitez implémenter.


Instancie Spécifiez un mode d’instanciation pour indiquer comment
votre objet COM sera chargé. Voir “Types d’instanciation des
objets COM” à la page 45-3 pour avoir des détails.
Remarque : Lorsque votre objet COM est utilisé uniquement en
tant que serveur en processus, le mode d’instanciation est
ignoré.
Modèle de Choisissez le modèle de thread pour indiquer comment les
thread applications client pourront appeler l’interface de votre objet
COM. Pour plus d’informations, voir “Choix d’un modèle de
thread” à la page 45-4.
Remarque : Le modèle de thread choisi détermine comment
l’objet est recensé. Vous devez être certain que
l’implémentation de votre objet respecte le modèle sélectionné.

45-2 Guide du développeur


Types d’instanciation des objets COM

Interfaces Spécifiez les noms des interfaces COM que cet objet COM doit
implémentées implémenter.
Description Entrez la description de l’objet COM que vous êtes en train de
créer.
Inclure la Cochez cette case pour générer une bibliothèque de types pour
bibliothèque de cet objet. Une bibliothèque de types contient les informations
types qui vous permettent d’exposer toute interface de l’objet, ainsi
que ses méthodes et ses propriétés, aux applications client. Le
fait de cocher cette case active automatiquement la case
Marquer l’interface OleAutomation.
Marquer Cochez cette case pour activer le code de marshaling généré
l’interface quand vous créez une bibliothèque de types. COM sait
OleAutomation comment effectuer un marshaling de tous les types compatibles
Automation dans la bibliothèque de types et peut définir les
proxy et stubs de façon à transmettre des paramètres aux
serveurs hors processus (.EXE). Pour plus d’informations, voir
“Le mécanisme du marshaling” à la page 44-8.

Types d’instanciation des objets COM


Remarque Le mode d’instanciation est ignoré lorsque votre objet COM est uniquement
utilisé en tant que serveur en processus.
Lorsque votre application COM crée un nouvel objet COM, il peut avoir un des
types d’instanciation suivants :

Instanciation Signification
Interne L’objet ne peut être créé que de manière interne. Une application externe
ne peut pas créer d’instance de l’objet directement. Par exemple, une
application de traitement de texte peut avoir un objet document qui ne
peut être créé qu’en appelant une méthode de l’application qui peut
créer l’objet document.
Instance unique Autorise uniquement une seule interface COM pour chaque exécutable
(application), de sorte que la création de plusieurs instances entraîne la
création de plusieurs applications. Instance unique spécifie qu’aussitôt
qu’une application s’est connectée à l’objet, celui-ci est retiré de la vue
publique afin qu’aucune autre application ne puisse s’y connecter. Cette
option est souvent utilisée pour les applications MDI (interface à
documents multiples). Lorsqu’un client requiert les services d’un objet à
instance unique, toutes les demandes sont gérées par le même serveur.
Par exemple, chaque fois qu’un utilisateur demande d’ouvrir un nouveau
document dans une application de traitement de texte, le nouveau
document s’ouvre habituellement dans le même processus d’application.
Instance multiple Spécifie que plusieurs applications peuvent se connecter à l’objet. Chaque
fois qu’un client demande un service, une nouvelle instance du serveur
est invoquée. (C’est-à-dire qu’il peut y avoir plusieurs instances dans un
seul exécutable.) Chaque fois qu’un utilisateur essaie d’ouvrir
l’Explorateur de Windows, un Explorateur distinct est créé.

Création d’un objet COM simple 45-3


Choix d’un modèle de thread

Choix d’un modèle de thread


Lors de la création d’un objet à l’aide de l’expert, sélectionnez le modèle de
thread que votre objet peut supporter. En ajoutant le support des threads à votre
objet COM, vous pouvez augmenter ses performances.
Le tableau 45.1 présente la liste des différents modèles de threads que vous pouvez
spécifier.

Tableau 45.1 Modèles de threads pour les objets COM


Modèle de
thread Description Implémentation pros et cons
Unique Pas de support des threads. Les Les clients sont gérés un à la fois,
demandes client sont sérialisées aucun support des threads n’est
par le mécanisme d’appel. nécessaire.
Pas de gain en performances.
Apartment (ou Les clients peuvent appeler les Les données de l’instance sont
apartment à thread méthodes d’un objet uniquement sécurisées, les données globales
unique) depuis le thread sur lequel doivent être protégées en utilisant
l’objet a été créé. Différents les sections critiques ou une autre
objets du même serveur peuvent forme de sérialisation.
être appelés sur différents Les variables locales du thread sont
threads, mais chaque objet est fiables entre plusieurs appels.
appelé uniquement sur un seul
thread. Quelques gains en performances.
Les objets sont faciles à écrire, mais
les clients peuvent s’avérer plus
délicats.
Principalement utilisé pour les
contrôles des navigateurs Web.
Libre (également Les clients peuvent appeler Les objets doivent protéger toutes
appelé apartment à toutes les méthodes d’un objet les données de l’instance et les
threads multiples) depuis n’importe quel thread à données globales en utilisant les
n’importe quel moment. Les sections critiques ou une autre
objets peuvent gérer un nombre forme de sérialisation.
quelconque de threads à tout Les variables locales des threads ne
moment. sont pas fiables entre plusieurs
appels.
Les clients sont faciles à écrire,
mais les objets sont plus difficiles.
Principalement utilisé pour les
environnements DCOM distribués.
Les deux Les objets peuvent supporter Maximum de performances et de
des clients utilisant les modèles souplesse.
de threads apartment ou libre. Supporte les deux modèles de
threads lorsque les clients utilisent
à la fois le thread unique et le
mode libre afin d’améliorer les
performances.

45-4 Guide du développeur


Choix d’un modèle de thread

Le côté client et le côté serveur de l’application indiquent à COM les règles


qu’ils ont l’intention de suivre dans l’utilisation des threads au moment où ils
initialisent COM. COM compare les règles de types de threads acceptées par
chaque partie. Si les deux parties supportent les mêmes règles, COM établit une
connexion directe entre les deux et s’assure que les deux parties respectent bien
ces règles. Si les deux parties affichent des règles différentes, COM utilise le
marshaling entre les parties de sorte que chacune ne voit que les règles dont
COM sait qu’elle en connaît le traitement. Bien sûr, le marshaling affecte quelque
peu les performances, mais il permet de faire fonctionner ensemble des parties
utilisant des modèles de threads différents.
Remarque Le modèle de thread que vous choisissez dans l’expert détermine comment
l’objet est publié dans le registre. Vous devez être certain que l’implémentation
de l’objet est conforme au modèle de thread choisi.
Le modèle de thread n’est valide que pour les serveurs en processus. La
définition d’un modèle de thread dans l’expert définit la clé modèle de thread
dans l’entrée du registre CLSID, InProcessServer32.
Les serveurs hors processus sont recensés en tant qu’EXE et l’implémentation du
modèle de thread est à votre charge. Si l’EXE contient un serveur sans thread,
Delphi initialise COM pour une activité sans thread, ce qui signifie qu’il permet
de gérer n’importe quel objet libre de thread ou apartment contenu dans l’EXE.
Pour modifier manuellement le modèle de thread dans les EXE, voir la variable
CoInitFlags dans l’aide en ligne.
Remarque Quel que soit le modèle de thread, les variables locales sont toujours sécurisées.
En effet, les variables locales sont stockées sur la pile et chaque thread possède
sa propre pile.

Ecriture d’un objet supportant le modèle de thread libre


Utilisez le modèle de thread libre au lieu du modèle apartment chaque fois que
l’objet doit être accédé par plusieurs threads. Considérons, par exemple, une
application client connectée à un objet sur une machine distante. Lorsque le
client distant appelle une méthode sur cet objet, le serveur reçoit l’appel sur un
thread appartenant au groupe de threads de la machine serveur. Le thread
recevant effectue l’appel localement sur l’objet réel et, l’objet supportant le
modèle de thread libre, le thread peut effectuer un appel direct dans l’objet.
Si, au contraire, l’objet avait supporté le modèle de thread apartment, l’appel
aurait dû être transféré au thread dans lequel l’objet a été créé et le résultat
aurait dû être transféré en retour dans le thread recevant avant de revenir au
client. Cette approche nécessite des mécanismes de marshaling.
Pour supporter le modèle de thread libre, vous devez considérer la façon dont
chaque méthode accédera aux données d’instance. Si la méthode est écrite pour
des données d’instance, vous devez utiliser les sections critiques, ou tout autre
forme de sérialisation, pour protéger les données d’instance. En outre, la
sérialisation des appels critiques est moins lourde que l’exécution du code de
marshaling de COM.

Création d’un objet COM simple 45-5


Recensement d’un objet COM

Remarquez que si les données d’instance sont accessibles en lecture seulement, la


sérialisation n’est pas nécessaire.

Ecriture d’un objet supportant le modèle de thread


apartment
Pour implémenter le modèle de thread apartment (à thread unique), il est
nécessaire de suivre certaines règles :
• Le premier thread de l’application qui a été créé est le thread principal de
COM. C’est habituellement le thread sur lequel WinMain a été appelé. Ce doit
être également le dernier thread à désinitialiser COM.
• Chaque thread du modèle de thread apartment doit posséder une boucle de
messages et la file des messages doit être vérifiée fréquemment.
• Lorsqu’un thread obtient un pointeur sur une interface COM, les méthodes de
cette interface ne peuvent être appelées que depuis ce thread.
Le modèle apartment à thread unique est un moyen terme entre la solution qui
consiste à ne fournir aucun support des threads et celle qui assure le support
complet du multi-thread dans le modèle libre. Un serveur conforme au modèle
apartment est la garantie que le serveur sérialise les accès à toutes ses données
globales (par exemple, son nombre d’objets). En effet, différents objets peuvent
tenter d’accéder aux données globales depuis différents threads. Cependant, les
données des instances de l’objet sont sécurisées, car les méthodes sont toujours
appelées sur le même thread.
Habituellement, les contrôles destinés aux navigateurs Web utilisent le modèle de
thread apartment, car les applications navigateur initialisent toujours leurs
threads en tant qu’apartment.
Pour avoir des informations générales sur les threads, voir chapitre 8, “Ecriture
d’applications multithreads”.

Recensement d’un objet COM


Après avoir créé votre objet COM, vous devez en effectuer le recensement afin
que les autres applications puissent le trouver et l’utiliser.
Remarque Avant de supprimer un objet COM de votre système, vous devez le dé-recenser.
Pour recenser un objet COM,
• Choisissez Exécuter|Recenser le serveur ActiveX.
Pour dérecenser un objet COM,
• Choisissez Exécuter|Dérecenser le serveur ActiveX.
Il existe une alternative : vous pouvez utiliser la commande tregsvr depuis la
ligne de commande ou exécuter regsvr32.exe dans le système d’exploitation.

45-6 Guide du développeur


Test d’un objet COM

Test d’un objet COM


La procédure de test de votre objet COM dépend de la nature de l’objet COM
que vous avez conçu. Lorsque celui-ci est créé, testez-le en utilisant les interfaces
que vous avez implémentées pour accéder aux méthodes de ces interfaces.
Pour tester et déboguer un objet COM :
1 Si nécessaire, activez les informations sur le débogage à l’aide de l’onglet
Compilateur de la boîte de dialogue Projet|Options. Activez également
Débogage intégré dans la boîte de dialogue Outils|Options du débogueur.
2 Pour un serveur en processus, choisissez Exécuter|Paramètres.
3 Dans la zone Application hôte, tapez le nom de l’application client qui fera
appel au service de cet objet COM et choisissez OK.
4 Choisissez Exécuter|Exécuter.
5 Définissez des points d'arrêt dans l’objet COM.
6 Utilisez l’application client pour interagir avec l’objet COM.
L’objet COM est suspendu lorsque les points d'arrêt sont atteints.

Création d’un objet COM simple 45-7


45-8 Guide du développeur
Chapitre

Création d’un contrôleur


Chapter 46
46
Automation
L’Automation est un protocole COM qui définit la façon dont une application
accède à un objet résidant dans une autre application ou DLL. Un contrôleur
Automation est une application client qui contrôle un serveur Automation via un
ou plusieurs objets fournis par le serveur qui implémentent l’interface IDispatch.
Il est possible d’écrire des contrôleurs Automation dans tout langage ayant une
implémentation de COM et de l’automation. Actuellement, la plupart des
contrôleurs Automation sont écrits en C++, en Pascal Objet (Delphi) ou avec
Visual Basic.
Microsoft Word, Microsoft Excel et Internet Explorer sont des exemples de
serveurs Automation. Ces applications peuvent être contrôlées par des
applications Delphi ou par d’autres contrôleurs Automation.
Delphi vous donne la possibilité d’intégrer des applications et des DLL à une
variété d’applications en tant que serveurs ou contrôleurs Automation.
Ce qui suit est une présentation générale de la façon de créer un contrôleur
Automation dans l’environnement Delphi. Vous n’y trouverez pas les détails de
l’écriture d’application contrôleur pour chaque type de serveur. Pour cela,
consultez la documentation qui accompagne votre application serveur.
Pour des informations sur la création de serveurs Automation, voir chapitre 47,
“Création d’un serveur Automation”.
Sous Delphi, vous créez un contrôleur Automation en important la bibliothèque
de types d’un serveur Automation et en l’installant sur la palette de composants.

Création d’un contrôleur Automation 46-1


Création d’un contrôleur Automation en important une bibliothèque de types

Création d’un contrôleur Automation en important


une bibliothèque de types
Vous pouvez créer un contrôleur Automation en important la bibliothèque de
types d’un serveur Automation et en utilisant les classes générées
automatiquement pour contrôler le serveur Automation. A l’aide de la boîte de
dialogue d’importation de bibliothèque de types, vous installez sur la palette de
composants le serveur que décrit la bibliothèque de types, ce qui vous permet de
vous connecter au serveur et de connecter ses événements à l’aide de l’inspecteur
d’objet. Vous pouvez ensuite programmer les propriétés du serveur.
Pour importer la bibliothèque de types dans un contrôleur Automation,
1 Choisissez Projet|Importer une bibliothèque de types.
2 Sélectionnez la bibliothèque de types dans la liste.
La boîte de dialogue répertorie toutes les bibliothèques recensées sur ce
système. Si la bibliothèque de types n’est pas dans la liste, choisissez le
bouton Ajouter, trouvez et sélectionnez le fichier de la bibliothèque de types
(fichier .TLB) choisissez OK et répétez l’étape 2. Remarquez que la
bibliothèque de types peut être un fichier bibliothèque de types autonome
(.TLB, .OLB) ou un serveur fournissant une bibliothèque de types (.DLL,
.OCX, .EXE).
3 Choisissez la page de la palette sur laquelle ce serveur résidera.
4 Cochez la case Générer le wrapper de composants, afin de créer une
enveloppe TComponent qui vous permet d’installer le serveur décrit par la
bibliothèque de types sur la palette de composants.
5 Choisissez Installer.
Spécifiez l'emplacement où vous souhaitez que réside la bibliothèque de types
(dans un paquet existant ou dans un nouveau paquet). Ce bouton est estompé
si aucun composant ne peut être créé pour cette bibliothèque de types .
Un serveur décrit par cette bibliothèque de types réside désormais sur la palette
de composants. Vous pouvez utiliser l'inspecteur d'objet pour écrire un
gestionnaire d’événement pour le serveur.
Pour contrôler le serveur Automation par le biais de ce contrôleur, vous ajoutez
du code à l'unité d'implémentation afin d'assurer la prise en charge de la liaison
avancée (VTable) et de la liaison tardive (de répartition). Cette unité que vous
venez de créer inclut les enveloppes d'interface pour la liaison VTable de toutes
les interfaces exposées et la liaison de répartition des interfaces doubles et de
répartition.

46-2 Guide du développeur


Création d’un contrôleur Automation en important une bibliothèque de types

Gestion des événements dans un contrôleur


Automation
Après avoir installé un serveur sur la palette de composants, vous pouvez
utiliser l'inspecteur d'objet pour écrire un gestionnaire d'événement.
Pour gérer les événements,
1 A partir de la palette de composants, placez le composant serveur souhaité
sur votre fiche.
2 Sélectionnez le composant et cliquez sur l'onglet Evénements de l'inspecteur
d'objet. Une liste d'événements de l'objet apparaît.
3 Double-cliquez sur l’espace à droite d’un événement afin que Delphi ouvre
l’éditeur de code et affiche le gestionnaire d’événement squelette que vous
pouvez ensuite compléter.
Après avoir implémenté votre gestionnaire d’événement, vous pouvez vous
connecter à un serveur.

Connexion à un serveur et déconnexion d’un serveur


Généralement, vous vous connectez à un serveur par le biais de son interface
principale. Par exemple, vous vous connectez à Microsoft Word par le biais du
composant WordApplication. Une fois connecté à l'interface principale, vous
pouvez vous connecter à tous les composants de l'application (comme
WordDocument ou WordParagraphFormat) à l'aide de la méthode ConnectTo .
Voici le gestionnaire de l'événement ExcelApplication, OnNewWorkBook.
Nous affectons le dossier au composant ExcelWorkbook.
procedure TForm1.XLappNewWorkbook(Sender: TObject; var Wb:OleVariant);
begin
ExcelWorkbook1.ConnectTo((iUnknown(wb) as ExcelWorkBook));
end;
Après avoir importé la bibliothèque de types, ajoutez du code à l’unité
d’implémentation pour contrôler le serveur avec une interface double (le plus
courant) ou avec une interface de répartition.
Remarque Les fichiers d’importation créés lors de l’importation d’une bibliothèque de types
(libname_TLB.CPP et libname_.H) doivent être considérés comme étant en lecture
seule et ne sont pas conçus pour être modifiés de quelque manière que ce soit.
Ces fichiers sont régénérés chaque fois que la bibliothèque de types est
actualisée, et toutes les modifications sont écrasées.
Pour les serveurs qui exposent une méthode Quit (comme WordApplication et
ExcelApplication), du code est généré pour appeler cette méthode dans la
méthode Disconnect. Généralement, Quit expose une fonctionnalité équivalente
au fait de cliquer sur Fichier pour quitter l'application. Si AutoConnect vaut
True, le serveur d'applications quitte en même temps que le client. En
conséquence, enfoncer la touche F1 lorsque AutoQuit est mis en surbrillance
dans l'inspecteur d'objet est sans effet.

Création d’un contrôleur Automation 46-3


Création d’un contrôleur Automation en important une bibliothèque de types

Contrôle d’un serveur Automation avec une interface


double
Lorsque vous créez un contrôleur Automation en sélectionnant un objet à partir
de la palette de composants, il fournit automatiquement une interface double car
Delphi peut déterminer la disposition VTable à partir des informations contenues
dans la bibliothèque de types. En appelant une méthode sur la classe, vous vous
connectez automatiquement à une instance de Word.
Par exemple, pour un serveur Word, vous appelez une méthode de classe
TWordApplication comme suit :
foo := TWordapplication
foo.DoSomething;
Bien sûr, la façon antérieure consistant à contrôler un serveur Automation avec
une interface double fonctionne toujours, mais elle est plus contraignante. Vous
devez d'abord déclarer une interface et l'initialiser avec la méthode Create d'une
classe proxy client CoClass. Vous appelez ensuite les méthodes de l’objet
interface intelligent. Par exemple,
foo : _Application
foo := CoWordApplication.Create
foo.DoSomething;
L’interface et la classe proxy client CoClass sont définies dans l’unité générée
automatiquement lorsque vous importez une bibliothèque de types. Les noms
des interfaces intelligentes commencent par “I”. Par exemple, l’interface
intelligente principale de Microsoft Word est “IWordBasic”. Les noms des classes
proxy client CoClasse commencent par “Co”. Ainsi le nom de la principale classe
proxy client CoClass de Microsoft Word s’appelle CoApplication.
Pour plus d’informations sur les interfaces doubles, voir “Interfaces Automation”
à la page 47-7.

Contrôle d’un serveur Automation en utilisant


interface de répartition
Généralement, vous utilisez l’interface double pour contrôler le serveur
Automation, comme décrit ci-dessus. Toutefois, il se peut que vous deviez
contrôler un serveur Automation avec un objet interface de répartition intelligent.
Pour ce faire,
1 Dans l’unité de l’implémentation du contrôleur Automation, déclarez une
dispinterface .
2 Contrôlez le serveur Automation en appelant les méthodes de l’objet interface
de répartition.
Pour plus d’informations sur les interfaces de répartition, voir “Interfaces
Automation” à la page 47-7.

46-4 Guide du développeur


Création d’un contrôleur Automation en important une bibliothèque de types

Exemple : impression d’un document dans Microsoft


Word
Les étapes suivantes illustrent la manière de créer un contrôleur Automation
imprimant un document avec Microsoft Word 8 d’Office 97.
Avant de commencer, créez un nouveau projet composé d’une fiche, d’un bouton
et d’une boîte de dialogue Ouvrir (TOpenDialog). Ces contrôles constituent le
contrôleur Automation.

Etape 1 : Préparation de Delphi pour cet exemple


Pour vous faciliter la tâche, Delphi fournit de nombreux serveurs courants sur la
palette de composants, comme Word, Excel et Powerpoint. Pour illustrer
l'importation d'un serveur, nous utilisons Word. Puisqu'il existe déjà sur la
palette de composants, la première étape consiste à demander à l'utilisateur de
supprimer le paquet contenant Word afin qu'il puisse l'installer sur la palette.
L'étape 4 décrit comment restituer à la palette de composants son état ordinaire.
Pour supprimer Word de la palette de composants,
1 Choisissez Composant|Installer des paquets.
2 Cliquez sur les composants serveur Automation exemple de Borland et
choisissez Supprimer.
La page Serveurs de la palette de composants ne contient plus aucun serveur
fourni avec Delphi (si aucun autre serveur n'a été importé, la page Serveurs
disparaît également).

Etape 2 : importation de la bibliothèque de types Word


Pour importer la bibliothèque de types Word,
1 Choisissez Projet|Importer une bibliothèque de types.
2 Dans la boîte de dialogue d’importation de bibliothèque de types,
1 Sélectionnez la bibliothèque d’objets Microsoft Office 8.0.
Si Word (version 8) n’est pas dans la liste, choisissez le bouton Ajouter,
puis dans le dossier Program Files\Microsoft Office\Office, sélectionnez le
fichier de la bibliothèque de types de Word, MSWord8.olb, choisissez
Ajouter et sélectionnez Word (Version 8) dans la liste.
2 Dans Page de palette, choisissez Serveurs.
3 Choisissez Installer.
La boîte de dialogue d’installation apparaît. Sélectionnez l'onglet Dans
nouveaux paquets et tapez WordExample pour créer un nouveau paquet
contenant cette bibliothèque de types.

Création d’un contrôleur Automation 46-5


Création d’un contrôleur Automation en important une bibliothèque de types

3 Retournez à Serveurs dans Page de palette, sélectionnez WordApplication et


placez-le sur une fiche.
4 Ecrivez un gestionnaire d’événement pour chaque objet en choisissant l'onglet
Evénements de l'inspecteur d'objet, en double-cliquant sur l'espace en regard
de chaque événement, puis en modifiant le gestionnaire d'événement squelette
dans l'éditeur de code.

Etape 3 : utilisation d’un objet interface VTable ou de


répartition pour contrôler Microsoft Word
Vous pouvez employer un objet interface VTable ou de répartition pour contrôler
Microsoft Word.

Utilisation d’un objet interface VTable


Pour utiliser un objet interface VTable afin de contrôler Microsoft Word, vous
ajoutez le code au gestionnaire d’événement OnClick du bouton du contrôleur
Automation. Pour ce faire, vous devez simplement appeler les méthodes de la
classe que vous venez de créer. Pour Word, il s'agit de la classe
TWordApplication.
Remarque Vous devez inclure la bibliothèque de types Word, Word_TLB, dans la clause
Uses du fichier d’implémentation du contrôleur Automation.
1 Créez et initialisez comme suit un objet Application en utilisant la classe
d’interface intelligente VTable_Application :
var
FileName: OleVariant;
begin
MyWord: _Application;
WordApplication1.Create;
2 Appelez la méthode PrintOut de l’objet WordBasic en utilisant l’opérateur “.”
(point) et en libérant l’objet avec Quit :
if OpenDialog1.Execute then
begin
FileName := OpenDialog1.Filename;
WordApplication1.Documents.Open(FileName,
EmptyParam,EmptyParam,EmptyParam,EmptyParam,
EmptyParam,EmptyParam,EmptyParam,EmptyParam,
EmptyParam);
WordApplication1.ActiveDocument.PrintOut(EmptyParam,
EmptyParam,EmptyParam,EmptyParam,EmptyParam,
EmptyParam,EmptyParam,EmptyParam,EmptyParam,
EmptyParam,EmptyParam,EmptyParam,EmptyParam,
EmptyParam);
WordApplication1.Quit(EmptyParam,EmptyParam,EmptyParam);
end;

46-6 Guide du développeur


Création d’un contrôleur Automation en important une bibliothèque de types

Utilisation d’un objet interface de répartition


Pour utiliser un objet interface de répartition (dispatch) afin de contrôler
Microsoft Word, effectuez les opérations suivantes en ajoutant le code au
gestionnaire d’événement OnClick du bouton du contrôleur Automation :
1 Créez et initialisez l’objet Application en utilisant la classe enveloppe de
répartition intelligente _ApplicationDisp et l’une de ses méthodes de liaison
comme suit :
var
MyWord: _ApplicationDisp;
FileName: OleVariant;
begin
MyWord:= CoApplication_.Create;
Remarque Dans la clause Uses du fichier d’implémentation du contrôleur Automation,
ajoutez Word_TLB pour inclure la bibliothèque de types Word.
2 Appelez les méthodes Open, PrintOut et File, et Quit.
if OpenDialog.Execute then
begin
FileName := OpenDialog1.Filename;
MyWord.Documents.Open(FileName,
EmptyParam,EmptyParam,EmptyParam,EmptyParam,
EmptyParam,EmptyParam,EmptyParam,EmptyParam,
EmptyParam);
MyWord.ActiveDocument.PrintOut(EmptyParam,
EmptyParam,EmptyParam,EmptyParam,EmptyParam,
EmptyParam,EmptyParam,EmptyParam,EmptyParam,
EmptyParam,EmptyParam,EmptyParam,EmptyParam,
EmptyParam);
MyWord.Quit(EmptyParam,EmptyParam,EmptyParam);
end;
if OpenDialog.Execute then
begin
FileName := OpenDialog1.Filename;
MyWord.Documents.Open(FileName,
EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,
EmptyParam,EmptyParam,EmptyParam,EmptyParam);
MyWord.ActiveDocument.PrintOut(EmptyParam,
EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,
EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,
EmptyParam,EmptyParam,EmptyParam);
MyWord.Quit(EmptyParam,EmptyParam,EmptyParam);
end;

Etape 4 : Nettoyage de l’exemple


A la fin de cet exemple, vous pouvez restituer à Delphi son état initial.
1 Supprimez les objets de cette page Serveurs :
1 Choisissez Composant|Configurer la palette et sélectionnez la page
Serveurs.

Création d’un contrôleur Automation 46-7


Création d’un contrôleur Automation en important une bibliothèque de types

2 Sélectionnez tous les objets que vous venez d’ajouter sur la page et
choisissez Cacher.
3 Choisissez l’option de suppression. La page Serveurs disparaît.
2 Retournez dans le paquet Composants Serveur automation exemple Borland :
1 Choisissez Composant|Installer des paquets.
2 Cliquez sur Ajouter, naviguez jusqu’au répertoire BIN de Delphi et
sélectionnez le paquet Composants Serveur automation exemple Borland.
Choisissez Ouvrir.

Autres sources d’informations


Pour avoir les informations les plus récentes sur l’interface de répartition,
l’interface double ou les classes proxy client CoClasse, consultez les
commentaires placés dans le fichier source généré automatiquement.

46-8 Guide du développeur


Chapitre

Création d’un serveur


Chapter 47
47
Automation
Un serveur Automation est une application qui expose ses fonctionnalités à des
applications client, appelées contrôleurs Automation, afin qu’elles puissent les
utiliser. Peuvent être contrôleurs toutes les applications qui supportent
l’Automation, comme les applications Delphi, Visual Basic ou C++Builder. Un
serveur Automation peut être une application ou une bibliothèque.
Nous allons voir comment créer un serveur Automation en utilisant l’expert
serveur Automation de Delphi. Vous pourrez alors exposer les propriétés et les
méthodes d’une application existante à un contrôleur Automation.
Voici les étapes de la création d’un serveur Automation à partir d’une
application existante :
• Créer un objet Automation pour l’application.
• Exposer les propriétés et méthodes de l’application à l’Automation.
• Recenser l’application comme serveur Automation.
• Tester et déboguer l’application.
Pour des informations sur la création d’un contrôleur Automation, voir chapitre 46,
“Création d’un contrôleur Automation.” Pour des informations générales sur la
technologie COM, voir chapitre 44, “Présentation des technologies COM.”

Création d’un objet Automation pour une application


Un objet Automation est une classe Pascal Objet descendant de TAutoObject,
gérant OLE Automation et s’exposant lui-même aux autres applications. Etant
donné que TAutoObject gère OLE Automation, tout objet qui en dérive profite
automatiquement de la gestion Automation. Pour créer un objet Automation,
utilisez l’expert objet Automation.

Création d’un serveur Automation 47-1


Création d’un objet Automation pour une application

Avant de créer un objet Automation, créez ou ouvrez le projet d’une application


contenant les fonctionnalités à exposer. Le projet peut être une application ou
une bibliothèque ActiveX, selon vos besoins.
Pour afficher l’expert Automation,
1 Choisissez Fichier|Nouveau.
2 Sélectionnez l’onglet ActiveX.
3 Double-cliquez sur l’icône Objet Automation.
Dans la boîte de dialogue de l’expert, spécifiez ce qui suit :

Nom CoClasse Spécifiez la classe dont vous voulez exposer les propriétés et
les méthodes aux applications client. (Delphi fait précéder ce
nom d’un T.)
Instancie Spécifiez un mode d’instanciation pour indiquer comment
lancer le serveur Automation. Pour plus d’informations, voir
“Types d’instanciation des objets COM” à la page 45-3.
Remarque : quand l’objet Automation est utilisé uniquement
comme serveur en processus, l’instanciation est ignorée.
Modèle de thread Choisissez le modèle de thread pour indiquer comment les
applications client peuvent appeler l’interface de l’objet. C’est
le modèle de thread que vous validez pour l’implémentation
de l’objet Automation. Pour plus d’informations sur les
modèles de thread, voir “Choix d’un modèle de thread” à la
page 45-4.
Remarque : le modèle de thread choisi détermine la façon
dont l’objet est recensé. Vous devez vous assurer que
l’implémentation de l’objet est conforme au modèle
sélectionné.
Créer le code Cochez cette case pour demander à l’expert d’implémenter
de support une interface distincte pour la gestion des événements de
d’événement l’objet Automation.

Une fois cette procédure terminée, une nouvelle unité est ajoutée au projet en
cours, elle contient la définition de l’objet Automation. De plus, l’expert ajoute
au projet une bibliothèque de types et l’ouvre. Vous pouvez alors exposer les
propriétés et les méthodes de l’interface via la bibliothèque de types comme
indiqué plus bas.
L’objet Automation implémente une interface duale, qui supporte à la fois la
liaison immédiate (à la compilation) via la vtable et la liaison différée (à
l’exécution) via l’interface IDispatch. Pour plus d’informations, voir “Interfaces
duales” à la page 47-7.

47-2 Guide du développeur


Gestion des événements de l’objet Automation

Gestion des événements de l’objet Automation


L’expert Automation génère automatiquement le code d’événement si vous
cochez l’option Générer le code de support dans la boîte de dialogue Expert
objet automation.
Pour qu'un serveur gère les événements, il propose une définition d'une interface
de sortie implémentée par un client. Le client détermine les interfaces de sortie
disponibles en interrogeant l'interface IConnectionPointContainer du serveur et
utilise des méthodes fournies par l'interface IConnectionPoint du serveur pour
transmettre au serveur un pointeur vers l'implémentation du client des
événements (appelé récepteur). Le serveur doit maintenir une liste de ces
récepteurs et appeler des méthodes sur eux lorsqu'un événement survient.
Quand vous sélectionnez Générer le code de support, Delphi génère
automatiquement le code nécessaire à la gestion de IConnectPoint et
IConnectPointContainer.

Exposition des propriétés, méthodes et événements


d’une application
Quand vous construisez le serveur Automation avec l’expert Automation, il
génère automatiquement une bibliothèque de types, qui fournit un moyen aux
applications hôtes de savoir ce que l’objet peut faire. Pour exposer les propriétés,
méthodes et événements d'une application, vous modifiez la bibliothèque de
types du serveur Automation comme décrit ci-dessous.

Exposition d’une propriété à l’Automation


Une propriété est une fonction membre qui définit l’état d’un objet, comme la
couleur ou la police. Par exemple, un contrôle bouton peut disposer d’une
propriété déclarée comme ceci :
property Caption: WideString;
Pour exposer une propriété à l’Automation :
1 Dans l’éditeur de bibliothèques de types, sélectionnez l’interface par défaut de
l’objet Automation.
L’interface par défaut doit avoir le même nom que l’objet Automation précédé
de la lettre “I”. Pour déterminer l'interface par défaut, dans l'éditeur de
bibliothèque de types, choisissez l'onglet CoClasse et Implémente, puis
recherchez dans la liste des interfaces implémentées celle signalée par la
mention “Défaut”.
2 Pour exposer une propriété en lecture/écriture, cliquez sur le bouton Propriété
de la barre d’outils ; sinon, cliquez sur la flèche située à côté du bouton
Propriété de la barre d’outils puis cliquez sur le type de propriété à exposer.

Création d’un serveur Automation 47-3


Exposition des propriétés, méthodes et événements d’une application

3 Dans la page Attributs, spécifiez le nom de la propriété.


4 Dans la page Paramètres, spécifiez le type renvoyé par la propriété et ajoutez
les paramètres appropriés.
5 Cliquez sur le bouton Rafraîchir de la barre d’outils.
Une définition et un squelette d’implémentation de la propriété sont insérés
dans le fichier unité de l’objet Automation.
6 Dans le squelette d’implémentation de la propriété, ajoutez (entre les
instructions try et finally) le code définissant la fonctionnalité souhaitée. Le
plus souvent, ce code appelle simplement une fonction existante de
l’application.

Exposition d’une méthode à l’Automation


Une méthode peut être une procédure ou une fonction. Pour exposer une
méthode à l’Automation,
1 Dans l’éditeur de bibliothèques de types, sélectionnez l’interface par défaut de
l’objet Automation.
L’interface par défaut doit avoir le même nom que l’objet Automation précédé
de la lettre “I”. Pour déterminer l'interface par défaut, dans l'éditeur de
bibliothèque de types, choisissez l'onglet CoClasse et Implémente, puis
recherchez dans la liste des interfaces implémentées celle signalée par la
mention “Défaut”.
2 Cliquez sur le bouton Méthode.
3 Dans la page Attributs, spécifiez le nom de la méthode.
4 Dans la page Paramètres, spécifiez le type renvoyé par la méthode et ajoutez
les paramètres appropriés.
5 Cliquez sur le bouton Rafraîchir de la barre d’outils.
Une définition et un squelette d’implémentation de la méthode sont insérés
dans le fichier unité de l’objet Automation.
6 Dans le squelette d’implémentation de la méthode, ajoutez entre les
instructions try et finally le code définissant la fonctionnalité souhaitée. Le
plus souvent, ce code appelle simplement une fonction existante de
l’application.

Exposition d’un événement à l’Automation


Pour exposer un événement à l’Automation,
1 Dans l’expert Automation, cochez la case Créer le code de support
d’événement.
L’expert crée un objet Automation qui inclut une interface d’événements.

47-4 Guide du développeur


Exposition des propriétés, méthodes et événements d’une application

2 Dans l’éditeur de bibliothèques de types, sélectionnez l’interface d’événements


de l’objet Automation.
L’interface d’événements doit avoir le même nom que l’objet Automation
précédé de la lettre “I” et suivi du mot “Events.”
3 Cliquez sur le bouton Méthode de la barre d’outils de la bibliothèque de types.
4 Dans la page Attributs, spécifiez le nom de la méthode, par exemple MyEvent.
5 Cliquez sur le bouton Rafraîchir de la barre d’outils.
Une définition et un squelette d’implémentation de l’événement sont insérés
dans le fichier unité de l’objet Automation.
6 Dans l’éditeur de code, créez un gestionnaire d’événement à l’intérieur du
descendant de TAutoObject de la classe de l’objet Automation. Par exemple,
unit ev;
interface
uses
ComObj, AxCtrls, ActiveX, Project1_TLB;
type
TMyAutoObject = class (TAutoObject,IConnectionPointContainer, IMyAutoObject)
private
ƒ
public
prodcure Initialize; override ;
procedure EventHandler; { Ajoute un gestionnaire d’événement}
7 A la fin de la méthode Initialize, assignez un événement au gestionnaire
d’événement que vous venez de créer. Par exemple,
procedure TMyAutoObject.Initialize;
begin
inherited Initialize;
FConnectionPoints:= TConnectionPoints.Create(Self);
if AutoFactory.EventTypeInfo <> nil then
FConnectionPoints.CreateConnectionPoint (AUtoFactory.EventIID,
ckSingle, EventConnect);
OnEvent = EventHandler;{ Assigne un événement au gestionnaire d’événement }
end;
8 Ajoutez le code nécessaire pour appeler la méthode implémentée par le
récepteur. Par exemple, placez le code suivant en remplaçant “MyEvent” par
le nom de votre événement.
procedure TMyAutoObject.EventHandler;
begin
if FEvents <> nil then FEvents.MyEvent;{ Appelle la méthode implémentée par le
récepteur.}
end;

Autres sources d’informations


Appuyez sur F1 n’importe où dans l’éditeur de bibliothèques de types pour
obtenir des informations sur l’utilisation de l’éditeur.

Création d’un serveur Automation 47-5


Recensement d’une application comme serveur Automation

Recensement d’une application comme serveur


Automation
Un serveur Automation peut être recensé comme serveur en ou hors processus.
Pour davantage d’informations sur ces types de serveur, voir “Serveurs en
processus, hors processus et distants” à la page 44-6.
Remarque Pour retirer le serveur Automation de votre système, il est recommandé de
commencer par retirer ses entrées du registre Windows.

Recensement d’un serveur en processus


Pour enregistrer un serveur en processus (DLL ou OCX) :
• Choisissez Exécuter|Recenser le serveur ActiveX.
Pour annuler le recensement d’un serveur en processus :
• Choisissez Exécuter|Dérecenser le serveur ActiveX.

Recensement d’un serveur hors processus


Pour recenser un serveur hors processus :
• Exécutez le serveur avec l’option de ligne de commande /regserver.
Pour spécifier les options de la ligne de commande, utilisez la boîte de
dialogue Exécuter|Paramètres.
Vous pouvez également recenser le serveur en l’exécutant.
Pour annuler le recensement d’un serveur hors processus :
• Exécutez le serveur avec l’option de ligne de commande /unregserver.

Test et débogage de l’application


Pour tester et déboguer un serveur Automation :
1 Si nécessaire, activez les informations de débogage en utilisant la page
Compilateur de la boîte de dialogue Projet|Options. Activez aussi Débogage
intégré dans le dialogue Outils|Options du débogueur.
2 Pour un serveur en processus, choisissez Exécuter|Paramètres, entrez le nom
du contrôleur Automation dans la zone Application hôte puis choisissez OK.
3 Choisissez Exécuter|Exécuter.
4 Définissez des points d’arrêts dans le serveur Automation.
5 Utilisez le contrôleur Automation pour interagir avec le serveur Automation.
Le serveur Automation fait une pause quand les points d’arrêts sont rencontrés.

47-6 Guide du développeur


Interfaces Automation

Interfaces Automation
Les experts de Delphi implémentent par défaut une interface duale, ce qui
signifie que l’objet Automation supporte à la fois :
• La liaison différée à l’exécution, via l’interface IDispatch. C’est l’interface de
répartition ou dispinterface.
• La liaison immédiate à la compilation, qui s’effectue via un appel direct à
l’une des fonctions membre de la table des fonctions virtuelles (VTable) de
l’objet. C’est l’interface personnalisée.

Interfaces duales
Une interface duale est à la fois une interface personnalisée et une dispinterface.
Elle est implémentée en tant qu’interface vtable COM qui dérive de IDispatch.
Pour les contrôleurs qui accèdent à l’objet uniquement pendant l’exécution, la
dispinterface est disponible. Pour les objets qui peuvent tirer profit de la liaison
à la compilation, c’est l’interface VTable la plus efficace qui est utilisée.
Les interfaces duales offrent les avantages combinés des interfaces VTable et des
dispinterfaces :
• Pour les interfaces VTable, le compilateur fait une vérification de type et
fournit des messages d’erreurs plus informatifs.
• Pour les contrôleurs Automation qui ne peuvent pas obtenir d’information de
type, la dispinterface fournit l’accès à l’objet à l’exécution.
• Pour les serveurs en processus, vous bénéficiez d’un accès rapide via les
interfaces VTable.
• Pour les serveurs hors processus, COMeffectue le marshaling des données à la
fois pour les interfaces VTable et pour les dispinterfaces. COM fournit une
implémentation proxy/stub générique qui peut réaliser le marshaling de
l’interface en fonction des informations contenues dans une bibliothèque de
types. Pour plus d’informations sur le marshaling, voir Marshaling des
données,” à la page 47-9.
Le diagramme page suivante représente une interface IMyInterface dans un objet
qui supporte une interface duale nommée IMyInterface. Les trois premières
entrées de la VTable d’une interface duale font référence à l’interface IUnknown,
les quatre suivantes font référence à l’interface IDispatch, les autres sont des
entrées COM pour l’accès direct aux membres de l’interface personnalisée.

Création d’un serveur Automation 47-7


Interfaces Automation

Figure 47.1 VTable d’une interface duale

Interfaces de répartition
Les contrôleurs Automation sont des clients qui utilisent l’interface COM
IDispatch pour accéder aux objets du serveur COM. Le contrôleur doit d’abord
créer l’objet, puis demander à l’interface IUnknown de l’objet un pointeur sur son
interface IDispatch. IDispatch garde en interne la trace des méthodes et des
propriétés par le biais d’un identificateur de répartition (dispID), qui est un
numéro d’identification propre à chaque membre interface. Par IDispatch, un
contrôleur récupère les informations de type de l’objet pour l’interface de
répartition, puis associe le nom des membres interface aux dispID spécifiques.
Ces dispID sont accessibles à l’exécution et les contrôleurs y accèdent en
appelant la méthode GetIDsOfNames de IDispatch.
Lorsqu’il connaît le dispID, le contrôleur peut appeler la méthode Invoke de
IDispatch pour exécuter le code requis (propriété ou méthode), en regroupant les
paramètres de la propriété ou de la méthode dans l’un des paramètres Invoke.
Invoke a une signature fixe définie lors de la compilation, qui lui permet
d’accepter des arguments variés lors de l’appel d’une méthode d’interface.
L’implémentation de l’objet automation de Invoke doit alors dégrouper les
paramètres, appeler la propriété ou la méthode et gérer les éventuelles erreurs.
Lorsque la propriété ou la méthode revient, l’objet retransmet la valeur renvoyée
au contrôleur.
Cette procédure est appelée liaison différée car le contrôleur se lie à la propriété
ou à la méthode lors de l’exécution de l’application plutôt que lors de sa compilation.

47-8 Guide du développeur


Marshaling des données

Interfaces personnalisées
Les interfaces personnalisées sont des interfaces définies par l’utilisateur qui
permettent aux clients d’appeler les méthodes de l’interface en fonction de leur
ordre dans la VTable et du type des arguments. La VTable contient les adresses
de toutes les propriétés et méthodes qui sont membres de l’objet, y compris les
fonctions membre des interfaces qu’il supporte. Si l’objet ne supporte pas
IDispatch, les entrées correspondant aux membres des interfaces personnalisées
de l’objet suivent immédiatement celles des membres de IUnknown.
Si l'objet possède une bibliothèque de types, vous pouvez accéder à l'interface
personnalisée via sa disposition VTable, que vous pouvez obtenir à l'aide de
l'éditeur de bibliothèque de types. Si l'objet possède une bibliothèque de types et
gère IDispatch, un client peut aussi obtenir les dispID de l’interface IDispatch et
se lier directement à un déplacement de la VTable. L’importateur de la
bibliothèque de types de Delphi (TLIBIMP) extrait les dispIDs à l’importation, ce
qui évite aux clients qui utilisent les enveloppes de dispinterfaces d’appeler
GetIDsOfNames; ces informations figurent déjà dans le fichier _TLB file. Toutefois,
les clients doivent appeler Invoke.

Marshaling des données


Pour les serveurs hors processus et distants, vous devez prendre en compte la
façon dont COM effectue le marshaling des données hors du processus en cours.
Vous pouvez effectuer le marshaling :
• Automatiquement, en utilisant l’interface IDispatch.
• Automatiquement, en créant une bibliothèque de types avec votre serveur et
en marquant l’interface avec le drapeau d’Automation Ole. COM sait
comment réaliser le marshaling de tous les types compatibles Automation de
la bibliothèque de types et peut implémenter les proxys et les stubs pour
vous. Certaines restrictions de types sont nécessaires pour permettre le
marshaling automatique.
• Manuellement, en implémentant toutes les méthodes de l’interface IMarshal.
Cela s’appelle le marshaling personnalisé.

Types compatibles avec l’Automation


Les résultats de fonctions et les types des paramètres des méthodes déclarés
dans les interfaces duales et de répartition doivent être des types compatibles
Automation. les types suivants sont compatibles Automation OLE :
• Les types autorisés prédéfinis, comme Smallint, Integer, Single, Double,
WideString. Pour la liste complète, voir “Types autorisés” à la page 49-24.
• Les types énumération définis dans une bibliothèque de types. Les types
énumération compatibles Automation OLE sont stockés dans des valeurs
32 bits et sont traités comme des valeurs de type Integer pour la transmission
des paramètres.

Création d’un serveur Automation 47-9


Marshaling des données

• Les types interface définis dans une bibliothèque de types qui sont sécurisés
Automation OLE, c’est-à-dire dérivés de IDispatch et contenant seulement des
types compatibles Automation OLE.
• Les types dispinterface définis dans une bibliothèque de types.
• IFont, IStrings et IPicture. Les objets utilitaires doivent être instanciés pour que
correspondent
• un IFont à un TFont
• un IStrings à un TStrings
• un IPicture à un TPicture
Les experts contrôle ActiveX et ActiveForm créent automatiquement ces objets
utilitaires lorsque c’est nécessaire. Pour utiliser les objets utilitaires, appelez
respectivement les routines globales GetOleFont, GetOleStrings, GetOlePicture.

Restrictions de type pour le marshaling automatique


Pour qu’une interface supporte le marshaling automatique, les restrictions
suivantes sont nécessaires. Lorsque vous modifiez votre objet Automation en
utilisant l’éditeur de bibliothèques de types, celui-ci applique ces restrictions :
• Les types doivent être compatibles pour la communication interplate-forme.
Par exemple, vous ne pouvez pas utiliser de structure de données (autrement
qu’en implémentant un autre objet propriété), ni d’argument non signé, ni de
chaîne large, etc.
• Les types chaîne doivent être transférés en tant que BSTR. PChar et AnsiString
ne peuvent pas subir de marshaling en toute sécurité.
• Tous les membres d’une interface duale doivent passer un HRESULT comme
valeur de retour de fonction.
• Les membres d’une interface duale qui ont besoin de renvoyer d’autres
valeurs doivent spécifier ces paramètres en tant que var ou out, en indiquant
un paramètre de sortie pour renvoyer la valeur de la fonction.
Remarque Une façon d’outrepasser les restrictions des types Automation est d’implémenter
une interface IDispatch distincte et une interface personnalisée. Vous pouvez ainsi
utiliser tous les types d’arguments. Cela signifie que les clients COM ont la
possibilité d’utiliser l’interface personnalisée, à laquelle les contrôleurs
Automation peuvent encore accéder. Mais, dans ce cas, il faut implémenter
manuellement le code de marshaling.

Marshaling personnalisé
Typiquement, vous utiliserez le marshaling automatique dans les serveurs hors
processus et distants parce que c’est le plus simple -- COM fait le travail à votre
place. Cependant, vous pouvez décider de fournir un marshaling personnalisé si
vous pensez que ses performances seront supérieures.

47-10 Guide du développeur


Chapitre

Création d’un contrôle


Chapter 48
48
ActiveX
Un contrôle ActiveX est un composant logiciel qui s’incorpore à et étend les
fonctionnalités d’une application hôte gérant les contrôles ActiveX, comme
C++Builder, Delphi, Visual dBASE, Visual Basic, Internet Explorer ou Netscape
Navigator.
Ainsi, Delphi est fourni avec plusieurs contrôles ActiveX : graphes, tableurs ou
graphiques. Vous pouvez ajouter ces contrôles à la palette des composants de
l’EDI et les utiliser comme tout composant VCL standard en les déposant dans
des fichiers et en définissant leurs propriétés avec l’inspecteur d’objets.
Vous pouvez également déployer sur le Web un contrôle ActiveX, en le
référençant dans des documents HTML et en le visualisant dans un navigateur
Web gérant ActiveX.
Ce qui suit est une présentation générale de la façon de créer un contrôle
ActiveX dans l’environnement Delphi. Vous n’y trouverez pas les détails
complets de l’écriture des contrôles ActiveX. Pour cela, consultez la
documentation Microsoft Developer’s Network (MSDN) ou recherchez sur le site
web de Microsoft des informations sur ActiveX.

Présentation de la création d’un contrôle ActiveX


Delphi propose deux experts pour le développement ActiveX :
• L’expert contrôle ActiveX vous permet de convertir un contrôle VCL prédéfini
ou personnalisé en contrôle ActiveX, en englobant le contrôle VCL dans une
enveloppe de classe ActiveX.

Création d’un contrôle ActiveX 48-1


Présentation de la création d’un contrôle ActiveX

• L’expert ActiveForm vous permet de créer de toutes pièces une nouvelle


application ActiveX. L’expert configure le projet et ajoute une fiche vide dont
vous pouvez commencer la conception en ajoutant des contrôles.
L’expert contrôle ActiveX génère une unité d’implémentation qui descend de
deux objets, TActiveXControl pour les spécificités ActiveX et l’objet VCL du
contrôle que vous avez choisi d’encapsuler. L’expert ActiveForm génère une
unité d’implémentation qui descend de TActiveXForm.
Pour créer un nouveau contrôle ActiveX, vous devez effectuer les étapes
suivantes :
1 Concevez et créez le contrôle VCL personnalisé qui sera la base de votre
contrôle ActiveX.
2 Utilisez l’expert contrôle ActiveX pour créer un contrôle ActiveX à partir d’un
contrôle VCL
ou,
Utilisez l’expert ActiveForm pour créer un contrôle ActiveX basé sur une fiche
VCL et destiné au déploiement sur le Web.
3 Utilisez l’expert de page propriétés ActiveX pour créer une ou plusieurs pages
de propriétés pour le contrôle (facultatif).
4 Associez une page de propriétés à un contrôle ActiveX (facultatif).
5 Recensez le contrôle ActiveX.
6 Testez le contrôle ActiveX avec toutes les applications cible possibles.
7 Déployez le contrôle ActiveX sur le Web.
Un contrôle ActiveX est constitué du contrôle VCL à partir duquel il est
construit, ainsi que des propriétés, méthodes et événements énumérés dans la
bibliothèque de types du contrôle.

Eléments d’un contrôle ActiveX


Un contrôle ActiveX implique plusieurs éléments qui ont chacun une fonction
spécifique. Ces éléments sont un contrôle VCL, des propriétés, des méthodes, des
événements et une ou plusieurs bibliothèques de types associées.

Contrôle VCL
Un contrôle ActiveX dans Delphi est simplement un contrôle VCL qui a été
rendu accessible aux applications et aux objets supportant les contrôles ActiveX.
Lorsque vous créez un contrôle ActiveX, vous devez d’abord concevoir ou
choisir le contrôle VCL à partir duquel vous allez le construire.
Remarque Les contrôles disponibles dans la liste de l’expert sont dérivés de TWinControl.
Certains contrôles, comme EditControl, sont recensés comme contrôles Non
ActiveX et n’apparaissent donc pas dans la liste.

48-2 Guide du développeur


Conception d’un contrôle ActiveX

Bibliothèque de types
Une bibliothèque de types contient les définitions des types du contrôle ; elle est
créée automatiquement par l’expert contrôle ActiveX. Ces informations de types,
qui fournissent plus de détails que l’interface, représentent pour les contrôles un
moyen de publier leurs services aux applications hôtes. Lorsque vous concevez
votre contrôle, les informations de la bibliothèque de types sont stockées dans un
fichier portant l’extension .TLB et un fichier Pascal correspondant contient les
conversions Pascal. Lorsque vous construisez le contrôle ActiveX, les
informations de la bibliothèque de types sont automatiquement compilées dans
la DLL du contrôle ActiveX en tant que ressource.

Propriétés, méthodes et événements


Les propriétés, événements et méthodes du contrôle VCL deviennent ceux du
contrôle ActiveX.
• Une propriété est un attribut, comme une couleur ou un libellé.
• Une méthode est la demande faite à un contrôle d’exécuter une action.
• Un événement est la notification du contrôle au conteneur que quelque chose
s’est produit.

Page de propriétés
La page de propriétés permet à l’utilisateur d’un contrôle de voir et de modifier
ses propriétés. Vous pouvez grouper plusieurs propriétés sur une page, ou
utiliser une page pour fournir une interface de type dialogue à une propriété.
Pour savoir comment créer des pages de propriétés, voir “Création d’une page
de propriétés pour un contrôle ActiveX” à la page 48-14.

Conception d’un contrôle ActiveX


Lorsque vous concevez un contrôle ActiveX, vous commencez par créer un
contrôle VCL personnalisé. Il sera la base de votre contrôle ActiveX. Pour plus
d’informations, voir partie IV, “Création de composants personnalisés.”
Que vous conceviez des contrôles ActiveX à partir de contrôles VCL existants ou
à partir de nouvelles fiches ActiveForms, n’oubliez pas que vous implémentez un
contrôle ActiveX qui sera incorporé dans une application ; ce contrôle n’est pas
une application en lui-même.
Pour cela, vous n’utiliserez pas de boîtes de dialogue élaborées ni d’autres
composants majeurs de l’interface utilisateur. Votre objectif est de fabriquer un
contrôle simple qui fonctionne dans l’application principale et suive les règles de
cette dernière.
Vous créerez un contrôle ActiveX à partir d’un contrôle VCL plutôt que d’une
fiche ActiveForm si vous disposez déjà d’un contrôle que vous voulez
simplement encapsuler dans l’enveloppe d’un contrôle ActiveX. Vous utiliserez
alors l’expert contrôle ActiveX, comme décrit dans “Génération d’un contrôle
ActiveX à partir d’un contrôle VCL” à la page 48-4.

Création d’un contrôle ActiveX 48-3


Génération d’un contrôle ActiveX à partir d’un contrôle VCL

Si vous créez une application ActiveX plus importante, utilisez l’expert


ActiveForm, comme décrit dans “Génération d’un contrôle ActiveX basé sur une
fiche VCL” à la page 48-7.Généralement, vous utiliserez une fiche ActiveForm
pour créer et fournir des applications ActiveX sur le Web.
Les experts implémentent toutes les interfaces ActiveX nécessaires en utilisant les
objets VCL TActiveXControl ou TActiveForm. Il ne reste qu’à implémenter les
éventuelles interfaces supplémentaires que vous avez ajoutées à votre contrôle.
Une fois que le contrôle est généré par l’un des experts, vous pouvez modifier ses
propriétés, méthodes ou événements, à l’aide de l’éditeur de bibliothèques de types.

Génération d’un contrôle ActiveX à partir d’un


contrôle VCL
Vous générez un contrôle ActiveX à partir d’un contrôle VCL en utilisant
l’expert contrôle ActiveX. Les propriétés, méthodes et événements du contrôle
VCL deviennent les propriétés, méthodes et événements du contrôle ActiveX.
Pour plus d’informations sur la création des contrôles VCL, voir la partie IV,
“Création de composants personnalisés.”
L’expert contrôle ActiveX place en fait une enveloppe de classe ActiveX autour d’un
contrôle VCL et construit le contrôle ActiveX qui contient l’objet. Cette enveloppe
ActiveX expose les capacités du contrôle VCL aux autres objets et serveurs.
Avant d’utiliser l’expert contrôle ActiveX, vous devez sélectionner le contrôle
VCL à partir duquel construire le contrôle ActiveX.
Pour afficher l’expert contrôle ActiveX,
1 Choisissez Fichier|Nouveau pour ouvrir la boîte de dialogue Nouveaux
éléments.
2 Sélectionnez l’onglet ActiveX.
3 Double-cliquez sur l’icône Contrôle ActiveX.
Dans l’expert, spécifiez ce qui suit :
Nom de classe Choisissez le contrôle VCL dans la liste déroulante. Par exemple,
VCL pour créer un contrôle ActiveX qui permette aux applications
client d’utiliser un objet TButton, sélectionnez TButton.
Pour les fiches ActiveForms, l’option classe VCL est estompée
puisque les fiches ActiveForms sont toujours basées sur
TActiveForm.
Nouveau nom L’expert fournit un nom par défaut que les clients utiliseront
pour identifier votre contrôle ActiveX. Changez ce nom si vous
voulez fournir un autre nom de classe OLE.
Unité L’expert fournit un nom par défaut à l’unité contenant le code
implémentation d’implémentation du comportement du contrôle ActiveX.
Acceptez ce nom par défaut ou tapez-en un autre.

48-4 Guide du développeur


Génération d’un contrôle ActiveX à partir d’un contrôle VCL

Nom du projet L’expert fournit un nom par défaut au projet de bibliothèque


ActiveX pour votre contrôle ActiveX, si aucun projet en cours
n’est ouvert. Si une bibliothèque ActiveX est ouverte, cette
option est désactivée.
Modèle de thread Choisissez le modèle de thread pour indiquer comment les
applications client appellent l’interface de votre contrôle. C’est
le modèle de thread que vous validez pour l’implémentation
dans le contrôle. Pour plus d’informations sur les modèles de
thread “Choix d’un modèle de thread” à la page 45-4.
Remarque : le modèle de thread choisi détermine la façon dont
l’objet est recensé. Vous devez vous assurer que
l’implémentation de l’objet est conforme au modèle sélectionné.

Spécifiez les options du contrôle ActiveX suivantes :


Recenser le contrôle Cochez cette case pour activer la licence des contrôles ActiveX
que vous concevez et distribuez (à moins que les contrôles ne
soient distribués gratuitement). L’expert génère une licence de
conception pour les concepteurs du contrôle et une licence
d’exécution pour les utilisateurs du contrôle.
Inclure les Cochez cette case pour inclure les informations de version
informations de dans le contrôle ActiveX, comme un copyright ou une
version description de fichier. Ces informations peuvent être
visualisées dans le navigateur. Spécifiez les informations de
version en choisissant Projet|Options et en sélectionnant la
page des informations de version. Cliquez sur le bouton
Aide de cette page pour plus d’informations sur chaque
paramètre.
Remarque : Les informations de version sont nécessaires
pour recenser un contrôle dans Visual Basic 4.0.
Inclure la boîte Quand cette case est cochée, une boîte A propos est intégrée
A propos dans le projet. Dans un environnement de développement,
l’utilisateur du contrôle peut afficher la boîte de dialogue A
propos. La boîte de dialogue A propos est une fiche distincte
qu’il est possible de modifier. Par défaut, cette boîte de
dialogue contient le nom du contrôle ActiveX, une image,
des informations de copyright et un bouton OK.

L’expert gère les fichiers suivants :


• Un fichier projet de bibliothèque ActiveX contenant le code nécessaire au
démarrage d’un contrôle ActiveX. Généralement, vous ne modifierez pas ce
fichier.
• Une bibliothèque de type (.TLB) qui définit et implémente les propriétés,
méthodes et événements que le contrôle ActiveX expose pour l’automation.
Pour plus d’informations sur les bibliothèques de types, voir chapitre 49,
“Utilisation des bibliothèques de types”.

Création d’un contrôle ActiveX 48-5


Définition des licences des contrôles ActiveX

• Une unité d’implémentation ActiveX qui définit et implémente le contrôle


ActiveX en utilisant le cadre de travail Delphi ActiveX (DAX).Une unité et une
fiche de boîte de dialogue A propos (si la case Inclure la boîte A propos est
cochée).

Définition des licences des contrôles ActiveX


La définition d’une licence pour un contrôle ActiveX consiste à fournir une clé
de licence à la conception, et à gérer la création de licences de manière
dynamique lorsque le contrôle est créé à l'exécution.
Pour fournir des licences de conception, l'expert ActiveX crée une clé pour le
contrôle stockée dans un fichier .LIC portant le même nom que le projet.
L’utilisateur du contrôle doit disposer d’une copie du fichier .LIC pour pouvoir
utiliser le contrôle dans son environnement de développement. Chaque contrôle
d’un projet pour lequel la case Recenser le contrôle est cochée dispose d’une
entrée distincte dans le fichier LIC.
Pour gérer la création de licences d'exécution, la licence est générée en
interrogeant le contrôle qui lui est associé (à la conception), stockée et transmise
au contrôle lors de sa création dans le contexte d'un fichier EXE. Lorsque la
licence d'exécution est transmise au contrôle, la licence de conception n'est plus
requise.
Les licences d’exécution pour Internet Explorer nécessitent un niveau
supplémentaire d'indirection car l'utilisateur peut visualiser le code source HTML
de toutes les pages Web et un contrôle ActiveX est copié sur son ordinateur
avant qu'il ne soit affiché. Pour créer des licences d'exécution pour les contrôles
utilisés sous Internet Explorer, vous devez d'abord générer un fichier paquet de
licence (fichier LPK) et l'incorporer dans la page HTML qui contient le contrôle.
Le fichier LPK est essentiellement un tableau de CLSID et de clés de licence.
Remarque Pour générer le fichier LPK, utilisez l’utilitaire LPK_TOOL.EXE, que vous pouvez
télécharger à partir du site Web de Microsoft (www.microsoft.com).
Pour incorporer le fichier LPK dans une page Web, utilisez les objets HTML,
<OBJECT> et <PARAM>, comme suit :
<OBJECT CLASSID="clsid:6980CB99-f75D-84cf-B254-55CA55A69452">
<PARAM NAME="LPKPath" VALUE="ctrllic.lpk">
</OBJECT>
Le CLSID identifie l'objet en tant que paquet de licence et PARAM spécifie
l'emplacement relatif du fichier paquet de licence par rapport à la page HTML.
Lorsque Internet Explorer essaie d'afficher la page Web contenant le contrôle, il
analyse le fichier LPK, extrait la clé de licence et, si celle-ci correspond à la licence
du contrôle, il rend le contrôle contenu dans la page. Si plusieurs fichiers LPK sont
inclus dans une page Web, Internet Explorer ne prend en compte que le premier.
Pour plus d’informations, effectuez des recherches sur la définition de licences de
contrôles ActiveX sur le site Web de Microsoft.

48-6 Guide du développeur


Génération d’un contrôle ActiveX basé sur une fiche VCL

Génération d’un contrôle ActiveX basé sur une fiche


VCL
L’expert ActiveForm génère un contrôle ActiveX basé sur une fiche VCL dont
vous effectuez la conception lorsque l’expert vous laisse dans le concepteur de
fiche. Vous pouvez utiliser la fiche ActiveForm pour créer des applications à
déployer sur le Web.
Lorsqu’une fiche ActiveForm est déployée, une page HTML est créée pour
contenir la référence à la fiche ActiveForm et spécifier son emplacement dans la
page. La fiche ActiveForm est ensuite affichée et exécutée depuis un navigateur
Web. Dans le navigateur Web, la fiche se comporte comme une fiche autonome
Delphi. Elle peut contenir n’importe quel composant VCL ou ActiveX, y compris
des contrôles VCL personnalisés.
Pour démarrer l’expert ActiveForm,
1 Choisissez Fichier|Nouveau pour ouvrir la boîte de dialogue Nouveaux
éléments.
2 Sélectionnez l’onglet ActiveX.
3 Double-cliquez sur l’icône ActiveForm.
Dans l’expert, spécifiez les options suivantes :

Nom de classe VCL Cette option est estompée puisque les fiches ActiveForms
sont toujours basées sur TActiveForm.
Nouveau nom L’expert fournit un nom par défaut que les clients utiliseront
pour identifier votre contrôle ActiveX. Changez ce nom si
vous voulez fournir un autre nom de classe OLE.
Unité L’expert fournit un nom par défaut à l’unité contenant le
implémentation code d’implémentation du comportement du contrôle
ActiveX. Acceptez ce nom par défaut ou tapez-en un autre.
Projet L’expert fournit un nom par défaut au projet de bibliothèque
ActiveX pour votre contrôle ActiveX, si aucun projet en
cours n’est ouvert. Si une bibliothèque ActiveX est ouverte,
cette option est désactivée.
Modèle de thread Choisissez le modèle de thread pour indiquer comment les
applications client appellent l’interface de votre contrôle.
C’est le modèle de thread que vous validez pour
l’implémentation dans le contrôle. Pour plus d’informations
sur les modèles de thread “Choix d’un modèle de thread” à
la page 45-4.
Remarque : le modèle de thread choisi détermine la façon
dont l’objet est recensé. Vous devez vous assurer que
l’implémentation de l’objet est conforme au modèle
sélectionné.

Création d’un contrôle ActiveX 48-7


Génération d’un contrôle ActiveX basé sur une fiche VCL

Spécifiez les options du contrôle ActiveX suivantes :

Recenser le contrôle Cochez cette case pour activer la licence des contrôles ActiveX
que vous concevez et distribuez (à moins que les contrôles ne
soient distribués gratuitement). L’expert génère une licence de
conception pour les concepteurs du contrôle et une licence
d’exécution pour les utilisateurs du contrôle.
Inclure les Cochez cette case pour inclure les informations de version
informations de dans le contrôle ActiveX, comme un copyright ou une
version description de fichier. Ces informations peuvent être
visualisées dans le navigateur. Spécifiez les informations de
version en choisissant Projet|Options et en sélectionnant la
page des informations de version. Cliquez sur le bouton
Aide de cette page pour plus d’informations sur chaque
paramètre.
Remarque : les informations de version sont nécessaires pour
recenser un contrôle dans Visual Basic 4.0.
Inclure la boîte Quand cette case est cochée, une boîte A propos est intégrée
A propos dans le projet. Dans un environnement de développement,
l’utilisateur du contrôle peut afficher la boîte de dialogue A
propos. La boîte de dialogue A propos est une fiche distincte
qu’il est possible de modifier. Par défaut, cette boîte de
dialogue contient le nom du contrôle ActiveX, une image,
des informations de copyright et un bouton OK.

L’expert gère les fichiers suivants :


• Un fichier projet de bibliothèque ActiveX contenant le code nécessaire au
démarrage d’un contrôle ActiveX. Généralement, vous ne modifierez pas ce
fichier.
• Une fiche.
• Une bibliothèque de type (.TLB) qui définit et implémente les propriétés,
méthodes et événements que le contrôle ActiveX expose pour l’automation.
Pour plus d’informations sur les bibliothèques de types, voir chapitre 49,
“Utilisation des bibliothèques de types”.
• Une unité d’implémentation ActiveX qui définit et implémente les propriétés,
méthodes et événements de TActiveForm en utilisant le cadre de travail Delphi
ActiveX (DAX).Une unité et une fiche de boîte de dialogue A propos (si la
case Inclure la boîte A propos est cochée).
A ce stade, l’expert ajoute une fiche vide à votre projet de bibliothèque ActiveX.
Vous pouvez alors y insérer des contrôles et concevoir la fiche à votre gré.
Une fois le projet ActiveForm conçu et compilé dans une bibliothèque ActiveX
(qui porte l’extension OCX), vous pouvez le déployer sur votre serveur Web ;
Delphi crée une page HTML de test avec une référence à la fiche ActiveForm.

48-8 Guide du développeur


Manipulation des propriétés, méthodes et événements d’un contrôle ActiveX

Manipulation des propriétés, méthodes et


événements d’un contrôle ActiveX
Les experts Contrôle ActiveX et ActiveForm génèrent une bibliothèque de types
qui définit et implémente les propriétés, méthodes et événements du contrôle ou
de la fiche VCL d’origine à l’exception des éléments suivants :
• Toute propriété, méthode ou événement utilisant un type non OLE.
• Toutes les propriétés orientées données.
Il peut donc être nécessaire d’ajouter manuellement certaines propriétés.
Remarque Comme les contrôles ActiveX ont un autre mécanisme pour rendre les contrôles
orientés données que les contrôles VCL, les experts ne convertissent pas les
propriétés relatives aux données. Vous pouvez ajouter certaines propriétés
orientées données ActiveX à votre contrôle, comme décrit dans “Activation de la
liaison de données simple avec la bibliothèque de types” à la page 48-12.
Vous pouvez ajouter, modifier ou supprimer les propriétés, méthodes et
événements d’un contrôle ActiveX en modifiant la bibliothèque de types par l’un
des moyens suivants :
• Choisir Edition|Ajouter à l’interface, dans l’EDI. Pour plus de détails, voir
“Ajout de propriétés, méthodes et événements supplémentaires” à la
page 48-9.
• Utiliser l’éditeur de bibliothèques de types, comme décrit dans le chapitre 49,
“Utilisation des bibliothèques de types”.
Remarque Toutes les modifications apportées à la bibliothèque de types sont perdues si
vous régénérez le contrôle ActiveX à partir du contrôle ou de la fiche VCL.
Remarque Les propriétés non publiées qui sont ajoutées manuellement à une bibliothèque
de types apparaissent dans l’environnement de développement mais les
modifications ainsi faites ne sont pas persistantes. Donc quand l’utilisateur du
contrôle modifie la valeur de la propriété, les modifications ne sont pas reflétées
quand le contrôle est exécuté. Si la source est un objet VCL alors que la
propriété n’est pas déjà publiée, vous devez créer un descendant de l’objet VCL
et publier la propriété dans le descendant.

Ajout de propriétés, méthodes et événements


supplémentaires
Vous pouvez ajouter au contrôle des propriétés, méthodes et événements
supplémentaires comme suit.
1 Choisissez Edition|Ajouter à l’interface. L’unité d’implémentation des
contrôles ActiveX doit être ouverte et sélectionnée pour que cet élément de
menu soit disponible.
Cela ouvre la boîte de dialogue Ajout à l’interface.

Création d’un contrôle ActiveX 48-9


Manipulation des propriétés, méthodes et événements d’un contrôle ActiveX

2 Choisissez Méthode ou Evénement dans la liste déroulante Interface. A côté


des propriétés, méthodes ou événements se trouve le nom de l’interface à
laquelle le membre sera ajouté.
3 Entrez la déclaration pour la propriété, la méthode ou l’événement. Par
exemple, si vous créez un contrôle ActiveX avec le contrôle TButton de la
VCL, vous pouvez ajouter la propriété suivante :
property Top:integer;
Si vous cochez la case Assistance syntaxe, des fenêtres surgissantes
apparaissent au fur et à mesure de votre saisie pour vous indiquer ce qui est
attendu à chaque endroit.
4 Choisissez OK.
La déclaration est automatiquement ajoutée à l’unité d’implémentation, au
fichier de bibliothèque de types (TLB) et à l’unité de bibliothèque de types du
contrôle. Ce que Delphi ajoute réellement dépend de ce que vous avez ajouté,
propriété, méthode ou événement.

Comment Delphi ajoute les propriétés


Comme l’interface est une interface double, les propriétés sont implémentées en
utilisant les méthodes d’accès en lecture et en écriture du fichier Pascal
(d’extension PAS). Pour ajouter une propriété Caption, l’expert ajoute réellement
les déclarations suivantes à l’unité d’implémentation :
property Caption: Integer read Get_Caption write Set_Caption;
property Caption: Integer read Get_Caption write Set_Caption;
Les déclarations et implémentations des méthodes d’accès en lecture et en
écriture de la propriété seraient :
function Get_Caption: Integer; safecall ;
procedure Set_Caption(Value: Integer); safecall ;
function TButtonX.Get_Caption: Integer;
begin
Result := FDelphiControl.Caption;
end;
procedure TButtonX.Set_Caption(Value: Integer);
begin
FDelphiControl.Caption := Value;
end;
Remarque Comme les méthodes d’une interface Automation sont déclarées safecall, vous
n’avez pas besoin d’implémenter le code d’exception COM pour ces méthodes —
le compilateur Delphi gère cela pour vous en générant autour du corps des
méthodes safecall le code permettant de capturer les exceptions Delphi et de les
convertir en structures d’information et en codes de retour des erreurs COM.
Les méthodes d’implémentation de l’interface transmettent simplement le
comportement au contrôle VCL. Dans certains cas, vous devez ajouter du code
pour convertir les types de données COM en types Delphi natifs.

48-10 Guide du développeur


Manipulation des propriétés, méthodes et événements d’un contrôle ActiveX

Comment Delphi ajoute les méthodes


Quand vous ajoutez une méthode, une implémentation vide est insérée à laquelle
vous ajoutez sa fonctionnalité. Par exemple, si vous ajoutez :
procedure Move;
La déclaration et le code suivants seront ajoutés à l’unité :
procedure Move; safecall ;
procedure TButtonX.Move;
begin
end;
Remarque Comme les méthodes d’une interface Automation sont déclarées safecall, vous
n’avez pas besoin d’implémenter le code d’exception COM pour ces méthodes —
le compilateur Delphi gère cela pour vous en générant, autour du corps des
méthodes safecall, le code permettant de capturer les exceptions Delphi et de les
convertir en structures d’information et en codes de retour des erreurs COM.

Comment Delphi ajoute les événements


Un contrôle ActiveX peut déclencher des événements vers son conteneur. Vous
ajoutez des événements pour spécifier lesquels peuvent être déclenchés par le
contrôle. Lorsque vous ajoutez un événement, les éléments suivants sont créés
pour chaque événement du contrôle :
• Des entrées pour la déclaration et l’implémentation d’une interface de
répartition, dans l’unité d’implémentation.
procedure KeyPressEvent(Sender: TObject; var Key: Char);
procedure TButtonX.KeyPressEvent(Sender: TObject; var Key: Char);
var
TempKey: Smallint;
begin
TempKey := Smallint(Key);
if FEvents <> nil then FEvents.OnKeyPress(TempKey);
Key := Char(TempKey);
end;
• Une entrée de bibliothèque de types pour l’interface d’événement,
dispinterface étant cochée sur sa page d’attributs.
• Une version Pascal Objet du fichier source de l’interface de la bibliothèque de
types.
Contrairement à l’interface Automation, l’interface d’événements n’est pas ajoutée
à la liste des interfaces du contrôle. Le contrôle ne reçoit pas ces événements,
c’est le conteneur qui les reçoit.

Création d’un contrôle ActiveX 48-11


Manipulation des propriétés, méthodes et événements d’un contrôle ActiveX

Activation de la liaison de données simple avec la


bibliothèque de types
Avec la liaison de données simple, vous pouvez lier une propriété de votre
contrôle ActiveX à un certain champ d’une base de données. Vous le faites en
définissant les indicateurs de liaison de la propriété à l’aide de l’éditeur de
bibliothèques de types.
Cette sectiondécrit comment lier les propriétés orientées données dans un
contrôle ActiveX. Pour plus d’informations sur la liaison des propriétés orientées
données dans un conteneur Delphi, voir “Activation de la liaison de données
simple des contrôles ActiveX dans le conteneur Delphi” à la page 48-13.
Lorsqu’un utilisateur modifie une propriété (comme un champ dans une base de
données) qui est marquée “bindable”, le contrôle notifie à la base de données
que la valeur a changé et demande que l’enregistrement soit actualisé. La base
de données notifie alors au contrôle la réussite ou l’échec de la mise à jour de
l’enregistrement.
Utilisez la bibliothèque de types pour activer la liaison de données simple,
1 Dans la barre d’outils, cliquez sur la propriété à lier.
2 Choisissez la page indicateurs.
3 Sélectionnez les attributs de liaison suivants :

Attributs
de liaison Description
Bindable Indique que la propriété supporte la liaison de données. Si elle est
marquée “bindable”, la propriété notifie à son conteneur que sa
valeur a été modifiée.
Request Edit Indique que la propriété supporte la notification OnRequestEdit.
Cela permet au contrôle de demander au conteneur si sa valeur
peut être modifiée par l’utilisateur.
Display Bindable Indique que le conteneur peut montrer aux utilisateurs que cette
propriété est “bindable”.
Default Bindable Indique la propriété “bindable” qui représente le mieux l’objet. Les
propriétés qui ont cet attribut de liaison par défaut doivent avoir
aussi l’attribut bindable. On ne peut pas spécifier plus d’une
propriété de ce type dans une dispinterface.
Immediate Bindable Chacune des propriétés “bindable” d’une fiche peut bénéficier de
ce comportement. Quand ce bit est défini, toutes les modifications
sont notifiées. Les bits Bindable et Request Edit doivent être définis
pour que cet attribut prenne effet.

4 Cliquez sur le bouton Rafraîchir de la barre d’outils, pour actualiser la


bibliothèque de types.
Pour pouvoir tester un contrôle dont la liaison de données est activée, vous
devez d’abord le recenser .

48-12 Guide du développeur


Manipulation des propriétés, méthodes et événements d’un contrôle ActiveX

Par exemple, pour lier un contrôle TEdit aux données d’un contrôle ActiveX,
créez le contrôle ActiveX à partir d’un TEdit puis modifiez les indicateurs de la
propriété Text en Bindable, Display Bindable, Default Bindable et Immediate
Bindable. Après le recensement et l’importation du contrôle, il peut être utilisé
pour afficher des données.

Activation de la liaison de données simple des


contrôles ActiveX dans le conteneur Delphi
Après avoir installé un contrôle ActiveX orienté données dans l’onglet ActiveX
de la palette, et placé le contrôle dans le concepteur de fiche, cliquez avec le
bouton droit de la souris sur le contrôle ActiveX orienté données pour afficher
une liste d’options. En plus des options normales du menu contextuel de la
fiche, apparaissent les éléments relatifs à la liaison de données.
Remarque Vous devez définir la propriété de source de données par le composant source
de données de la fiche avant d’appeler l’éditeur de liaisons de données. En
faisant cela, le dialogue fournit les champs Nom de champ et Propriété à partir
du composant source de données. L’éditeur ne montre que les propriétés du
composant source de données qui peuvent être des propriétés liées aux données
du contrôle ActiveX.
Pour lier un champ à une propriété,
1 Dans le dialogue Editeur de liaisons de données de contrôle ActiveX,
sélectionnez un champ et un nom de propriété.
Nom de champ présente la liste des champs de la base de données et Nom de
propriété la liste des propriétés du contrôle ActiveX qui peuvent être liées à
un champ d’une base de données. Le DispID de la propriété est entouré de
parenthèses, par exemple Value(12).
2 Cliquez sur Lier et sur OK.
Remarque Si aucune propriété n’apparaît dans le dialogue, c’est que le contrôle ActiveX ne
contient pas de propriété orientée données. Pour activer la liaison de données
simple pour une propriété d’un contrôle ActiveX, utilisez la bibliothèque de
types, comme décrit dans “Activation de la liaison de données simple avec la
bibliothèque de types” à la page 48-12.
L’exemple suivant illustre les étapes de l’utilisation d’un contrôle orienté données
dans le conteneur Delphi. Dans cet exemple, nous utilisons le contrôle calendrier
de Microsoft, qui est disponible si Microsoft Office 97 est installé sur votre système.
1 Depuis le menu principal de Delphi, choisissez Composant|Importer un
contrôle ActiveX.
2 Sélectionnez un contrôle ActiveX orienté données, comme le contrôle
calendrier 8.0 de Microsoft, changez son nom de classe en TCalendarAXControl
et cliquez sur Installer.
3 Dans le dialogue d’installation, cliquez sur OK pour ajouter le contrôle au
paquet utilisateur par défaut, ce qui le rend disponible dans la palette.

Création d’un contrôle ActiveX 48-13


Création d’une page de propriétés pour un contrôle ActiveX

4 Choisissez Tout fermer et Fichier|Nouvelle application, pour commencer une


nouvelle application.
5 Depuis l’onglet ActiveX, faites glisser sur la fiche l’objet TCalendarAXControl
que vous venez d’ajouter à la palette.
6 Depuis l’onglet AccèsBD, faites glisser sur la fiche un objet DataSource et un
objet Table.
7 Sélectionnez l’objet DataSource et définissez sa propriété DataSet par Table1.
8 Sélectionnez l’objet Table et faites ceci :
• Définissez la propriété DataBaseName par DBDEMOS
• Définissez la propriété TableName par EMPLOYEE.DB
• Définissez la propriété Active par True
9 Sélectionnez l’objet TCalendarAXControl et définissez sa propriété DataSource
par DataSource1.
10 Sélectionnez l’objet TCalendarAXControl, cliquez avec le bouton droit de la
souris et choisissez Liaisons de données pour appeler l’éditeur de liaisons de
données.
Nom de champ présente la liste de tous les champs de la base de données
active. Nom de propriété présente la liste des propriétés du contrôle ActiveX
qui peuvent être liées à un champ d’une base de données. Le DispID de la
propriété est entouré de parenthèses.
11 Sélectionnez le champ HireDate et la propriété Value, choisissez Lier puis OK.
Le nom du champ et la propriété sont désormais liés.
12 Depuis l’onglet ContrôleBD, faites glisser sur la fiche un objet DBGrid et
définissez sa propriété DataSource par DataSource1.
13 Depuis l’onglet ContrôleBD, faites glisser sur la fiche un objet DBNavigator et
définissez sa propriété DataSource par DataSource1.
14 Exécutez l’application.
15 Testez-la comme ceci :
Le champ HireDate étant affiché dans l’objet DBGrid, naviguez dans la base
de données en utilisant l’objet navigateur. Les dates du contrôle ActiveX
changent au fur et à mesure que vous vous déplacez dans la base de données.

Création d’une page de propriétés pour un contrôle


ActiveX
Une page de propriétés est une boîte de dialogue similaire à l’inspecteur d’objets
Delphi qui permet aux utilisateurs de modifier les propriétés d’un contrôle
ActiveX. Un dialogue de page de propriétés sert à regrouper plusieurs propriétés
d’un contrôle afin de les modifier ensemble ou à gérer des propriétés complexes.

48-14 Guide du développeur


Création d’une page de propriétés pour un contrôle ActiveX

En général, les utilisateurs accèdent à la page des propriétés en cliquant sur le


contrôle ActiveX avec le bouton droit de la souris et en choisissant Propriétés.
Le processus de création d’une page de propriétés est similaire à celui d’une
fiche :
1 Création d’une nouvelle page de propriétés.
2 Ajout de contrôles à la page de propriétés.
3 Association des contrôles de la page de propriétés aux propriétés d’un
contrôle ActiveX.
4 Connexion de la page de propriétés au contrôle ActiveX.
Remarque Quand des propriétés sont ajoutées à un contrôle ActiveX ou à une fiche
ActiveForm, vous devez publier les propriétés dont vous voulez qu’elles
persistent. Pour plus de détails, voir “Exposition des propriétés d’un contrôle
ActiveX” à la page 48-17.

Création d’une nouvelle page de propriétés


Pour créer une nouvelle page de propriétés, utilisez l’expert Page propriétés.
Pour créer une nouvelle page de propriétés,
1 Choisissez Fichier|Nouveau.
2 Sélectionnez l’onglet ActiveX.
3 Double-cliquez sur l’icône Page propriétés.
L’expert crée une nouvelle fiche et l’unité d’implémentation de la page de
propriétés.

Ajout des contrôles à une page de propriétés


Vous devez ajouter à la page de propriétés un contrôle pour chaque propriété du
contrôle ActiveX auquel l’utilisateur doit avoir accès.
Par exemple, la figure suivante illustre la configuration de la page de propriétés
pour un contrôle ActiveX MaskEdit.
Figure 48.1 Page de propriétés MaskEdit en mode conception

Création d’un contrôle ActiveX 48-15


Création d’une page de propriétés pour un contrôle ActiveX

La boîte liste permet à l’utilisateur de sélectionner parmi une liste de modèles


exemple. Les contrôles de saisie permettent à l’utilisateur de tester le masque
avant de l’appliquer au contrôle ActiveX.

Association des contrôles de la page de propriétés


aux propriétés du contrôle ActiveX
Une fois tous les contrôles nécessaires ajoutés à la page de propriétés, il faut
associer chaque contrôle à la propriété correspondante. Cette association
s’effectue en ajoutant du code à l’unité implémentant la page de propriétés.
Précisément, il faut ajouter du code aux méthodes UpdatePropertyPage et
UpdateObject.

Actualisation de la page de propriétés


Ajoutez du code à la méthode UpdatePropertyPage pour mettre à jour le contrôle
de la page de propriétés quand les propriétés du contrôle ActiveX changent.
Vous devez ajouter du code à la méthode UpdatePropertyPage pour actualiser la
page de propriétés avec les valeurs en cours des propriétés du contrôle ActiveX.
Par exemple, le code suivant actualise le contrôle boîte de saisie (InputMask) de
la page de propriétés avec la valeur en cours de la propriété EditMask du
contrôle ActiveX (OleObject) :
procedure TPropertyPage1.UpdatePropertyPage;
begin
{ Actualise les contrôles à partir de OleObjects }
InputMask.Text := OleObject.EditMask;
end;

Actualisation de l’objet
Ajoutez du code à la méthode UpdateObject pour mettre à jour la propriété
quand l’utilisateur modifie les contrôles de la page de propriétés. Vous devez
ajouter du code à la méthode UpdateObject afin de définir la nouvelle valeur des
propriétés du contrôle ActiveX.
Par exemple, le code suivant affecte la propriété EditMask d’un contrôle ActiveX
(OleObject) en utilisant la valeur du contrôle boîte de saisie (InputMask) de la
page de propriétés :
Remarque Incluez le fichier .TLB qui déclare l’interface ICoClassNameDisp.
procedure TPropertyPage1.UpdateObject;
begin
{ Actualise OleObjects à partir des contrôles }
OleObject.EditMask := InputMask.Text;
end;

48-16 Guide du développeur


Exposition des propriétés d’un contrôle ActiveX

Connexion d’une page de propriétés à un contrôle


ActiveX
Pour connecter une page de propriétés à un contrôle ActiveX,
1 Ajoutez DefinePropertyPage, avec la constante GUID de la page de propriétés
comme paramètre, à l’implémentation de la méthode DefinePropertyPages dans
l’implémentation des contrôles de l’unité. Par exemple,
proedure TButtonX.DefinePropertyPages(DefinePropertyPage: TDefinePropertyPage);
begin
DefinePropertyPage(Class_PropertyPage1);
end;
La constante GUID, Class_PropertyPage1, de la page de propriétés peut être
trouvée dans l’unité des pages de propriétés.
Le GUID est défini dans l’unité d’implémentation de la page de propriétés ; il
est généré automatiquement par l’expert Page propriétés.
2 Ajoutez l’unité de la page de propriétés à la clause uses de l’unité
d’implémentation des contrôles.

Exposition des propriétés d’un contrôle ActiveX


Les propriétés d’un contrôle ActiveX ou ActiveForm peuvent apparaître dans un
environnement de développement comme Visual Basic, Delphi ou C++Builder.
L’utilisateur du contrôle ou de la fiche peut alors manipuler visuellement les
propriétés du contrôle. Pour faire apparaître les propriétés, elles doivent être
définies dans la bibliothèque de types du contrôle. Pour rendre persistantes les
modifications des propriétés, elles doivent être publiées. Vous pouvez ajouter
manuellement des propriétés à la bibliothèque de types ou laisser Delphi les
ajouter automatiquement. Delphi ajoute automatiquement les propriétés publiées
à la bibliothèque de types.
Remarque Les propriétés non publiées ajoutées manuellement dans la bibliothèque de types,
apparaissent dans un environnement de développement mais les modifications
effectuées dessus ne sont pas persistantes. C’est-à-dire que si l’utilisateur du
contrôle modifie la valeur d’une propriété, cette modification n’est pas prise en
compte lors de l’exécution du contrôle.
Pour exposer une propriété d’un contrôle créé à l’aide de l’expert contrôle
ActiveX, allez à la source du contrôle VCL pour exposer les propriétés
nécessaires. Si la source est un objet VCL et que la propriété n’est pas déjà
publiée, vous devez créer un descendant de l’objet VCL et publier la propriété
dans le descendant. Par exemple, pour publier la propriété Align de TButton,
ajoutez le code suivant à l’unité d’implémentation du contrôle ActiveX :
TAlignButon = class (TButton)
published
property Align;
end;

Création d’un contrôle ActiveX 48-17


Recensement d’un contrôle ActiveX

Pour exposer une propriété d’une fiche ActiveForm,


1 Sélectionnez Edition|Ajouter à l’interface, pendant que l’unité
d’implémentation est ouverte dans l’éditeur.
2 Sélectionnez Propriété/Méthode dans la liste déroulante Type de procédure.
3 Entrez la déclaration de la propriété qui exposera la propriété du contrôle. Par
exemple, pour la propriété Caption d’un contrôle bouton, tapez :
property MyButtonCaption: WideString;
4 Choisissez OK.
Les méthodes get et set sont ajoutées à l’unité d’implémentation.
5 Ajoutez du code aux méthodes pour acquérir (get) et définir (set) la propriété.
Par exemple, pour le bouton précédent, le code pourrait être :
function TActiveFormX.Get_MyButtonCaption: WideString;
begin
Result := Button1.Caption;
end;
procedure TActiveFormX.Set_MyButtonCaption(const Value: WideString);
begin
Button1.Caption := Value;
end;
Comme une fiche ActiveForm est un contrôle ActiveX, une bibliothèque de types
lui est associée et vous pouvez lui ajouter d’autres propriétés ou méthodes. Pour
savoir comment faire, reportez-vous à “Manipulation des propriétés, méthodes et
événements d’un contrôle ActiveX” à la page 48-9.

Recensement d’un contrôle ActiveX


Une fois le contrôle ActiveX créé, il faut le recenser afin que d’autres
applications puissent le trouver et l’utiliser.
Pour recenser un contrôle ActiveX :
• Choisissez Exécuter|Recenser le serveur ActiveX.
Remarque Avant de retirer un contrôle ActiveX de votre système, vous devez annuler son
recensement.
Pour annuler le recensement d’un contrôle ActiveX :
• Choisissez Exécuter|Dérecenser le serveur ActiveX.
Comme alternative, vous pouvez utiliser tregsvr sur la ligne de commande ou
exécuter le regsvr32.exe du système d’exploitation.

48-18 Guide du développeur


Test d’un contrôle ActiveX

Test d’un contrôle ActiveX


Pour tester votre contrôle, ajoutez-le à un paquet et importez-le comme contrôle
ActiveX. Cette procédure ajoute le contrôle ActiveX à la palette des composants
de Delphi. Vous pouvez alors déposer le contrôle dans une fiche et le tester.
Votre contrôle doit également être testé avec toutes les applications cible utilisant
ce contrôle.
Pour déboguer le contrôle ActiveX, sélectionnez Exécuter|Paramètres et entrez le
nom de l’application client dans la boîte de saisie Application hôte.
Les paramètres s’appliquent ensuite à l’application hôte. La sélection de
Exécuter|Exécuter démarre l’application hôte ou client et permet de définir des
points d’arrêts dans le contrôle.

Déploiement d’un contrôle ActiveX sur le Web


Avant de pouvoir utiliser dans des clients Web les contrôles ActiveX que vous
avez créés, ils doivent être déployés sur le serveur Web. A chaque fois que vous
modifiez le contrôle ActiveX, vous devez le recompiler et le re-déployer afin que
les applications client voient les modifications.
Avant de déployer un contrôle ActiveX, vous devez disposer d’un serveur Web
répondant aux messages du client. Vous pouvez acheter un serveur Web proposé
par un tiers, comme le serveur Web Inprise livré avec Intrabuilder ou vous
pouvez créer votre propre serveur Web si vous utilisez une version de Delphi
fournie avec les composants socket.
Pour déployer un contrôle ActiveX :
1 Sélectionnez Projet|Options de déploiement Web.
2 Dans la page Projet, définissez dans la zone Répertoire destination
l’emplacement de la DLL du contrôle ActiveX (sous la forme d’un chemin sur
le serveur Web). Cela peut être un chemin d’accès local ou un chemin d’accès
UNC, par exemple, C:\INETPUB\wwwroot.
3 Définissez dans URL destination, l’emplacement sur le serveur Web (sous la
forme d’une URL) de la DLL du contrôle ActiveX (sans le nom de fichier), par
exemple, http://mymachine.inprise.com/. Voir la documentation de votre
serveur Web pour davantage d’informations sur la manière de procéder.
4 Définissez dans Répertoire HTML l’emplacement (sous la forme d’un chemin
d’accès) où doit être placé le fichier HTML contenant une référence au
contrôle ActiveX, par exemple, C:\INETPUB\wwwroot. Ce chemin d’accès
peut être un chemin d’accès standard ou un chemin d’accès UNC.
5 Définissez les options de déploiement Web souhaitées, comme décrit dans
“Paramétrage des options de déploiement Web” à la page 48-20.

Création d’un contrôle ActiveX 48-19


Déploiement d’un contrôle ActiveX sur le Web

6 Choisissez OK.
7 Choisissez Projet|Déploiement Web.
Cela crée la base du code de déploiement contenant le contrôle ActiveX dans
une bibliothèque ActiveX (ayant l’extension OCX). Selon les options que vous
avez spécifiées, cette base du code de déploiement peut aussi contenir un
fichier cabinet (ayant l’extension CAB) ou d’information (ayant l’extension
INF).
La bibliothèque ActiveX est placée dans le répertoire destination spécifié à
l’étape 2. Le fichier HTML porte le même nom que le projet mais avec
l’extension .HTM. Il est créé dans le répertoire HTML spécifié à l’étape 4. Le
fichier HTML contient une URL faisant référence à la bibliothèque ActiveX à
l’emplacement spécifié à l’étape 3.
Remarque Si vous voulez placer ces fichiers sur votre serveur Web, utilisez un utilitaire
externe comme ftp.
8 Appelez votre navigateur Web gérant ActiveX et visualisez la page HTML
ainsi créée.
Quand cette page HTML est visualisée dans le navigateur Web, la fiche ou le
contrôle est affiché et exécuté comme application incorporée dans le navigateur.
C’est-à-dire que la bibliothèque s’exécute dans le même processus que
l’application navigateur.

Paramétrage des options de déploiement Web


Avant de déployer un contrôle ActiveX, vous devez spécifier les options de
déploiement Web qui doivent être employées pour créer la bibliothèque ActiveX.
Les options de déploiement Web comprennent des paramètres permettant de :

Compresser les fichiers en Un cabinet est un seul fichier, portant généralement


définissant un fichier CAB l’extension CAB, qui stocke les fichiers compressés
dans une bibliothèque de fichiers. La compression de
ces fichiers réduit, en général, considérablement les
temps de téléchargement (jusqu’à 70%). Lors de
l’installation, le navigateur décompresse les fichiers
stockés dans une archive et les copie sur le système
de l’utilisateur. Les fichiers que vous déployez
peuvent être des archives .CAB compressées.

48-20 Guide du développeur


Déploiement d’un contrôle ActiveX sur le Web

Encoder les fichiers L’encodage permet à l’utilisateur du contrôle de


déterminer avec certitude qui a écrit le contrôle et si
le code n’a pas été modifié depuis la signature.
Chaque fichier déployé, y compris les paquets et les
fichiers supplémentaires, peut avoir une signature par
encodage.
La signature par encodage crée une signature digitale
qui est ajoutée au fichier. Les navigateurs Web gérant
ActiveX configurés pour un niveau maximum de
sécurité n’affichent pas les contrôles ActiveX
dépourvus de signature par encodage.
Remarque : Delphi ne fournit pas les clés privées et
les justificatifs nécessaires à la signature par encodage.
Des informations sur la manière d’obtenir ces fichiers
sont disponibles auprès de Microsoft.
Définir les options de Vous avez la possibilité de spécifier les options de
paquets déploiement pour chaque fichier paquet faisant partie
de votre déploiement. Ces paquets peuvent
individuellement être signés par encodage et placés
dans des fichiers .CAB. Les paquets livrés avec Delphi
sont déjà signés par encodage et portent la signature
Inprise.

Selon les combinaisons d’options de paquet, de compression de fichier CAB et


d’encodage choisies pour votre bibliothèque ActiveX, celle-ci sera un fichier OCX,
un fichier CAB contenant un fichier OCX ou un fichier INF. Pour plus de détails,
voir le tableau des combinaisons d’options ci-dessous.

Case à cocher Défaut des Options de déploiement Web


Si vous cochez cette case, les paramètres définis dans les pages Projet, Paquets,
Fichiers supplémentaires et Encodage de la boîte de dialogue Options de
déploiement Web sont enregistrés comme valeurs par défaut. Si vous ne la
cochez pas, les paramètres n’affectent que le projet ActiveX ouvert.
Pour rétablir les paramètres d’origine, supprimez ou renommez le fichier
DEFPROJ.DOF.

Fichier .INF
Si votre contrôle ActiveX dépend de paquets ou d’autres fichiers
supplémentaires, ces fichiers doivent être inclus quand vous déployez le contrôle
ActiveX. Quand le contrôle ActiveX est déployé avec des paquets ou des fichiers
supplémentaires, un fichier portant l’extension INF (pour INFormation) est
automatiquement créé. Ce fichier spécifie les divers fichiers nécessaires devant
être téléchargés et configurés pour que la bibliothèque ActiveX s’exécute. La
syntaxe du fichier .INF permet la désignation par URL de paquets ou de fichiers
supplémentaires devant être téléchargés.

Création d’un contrôle ActiveX 48-21


Déploiement d’un contrôle ActiveX sur le Web

Les options de déploiement Web sont affichées sur les pages suivantes et décrites
dans les sections ci-après :
• page Projet
• page Paquets
• page Fichiers supplémentaires
• page Encodage

Combinaisons d’options
Le tableau suivant résume le résultat de la combinaison des différentes options
de déploiement Web concernant les paquets, la compression de fichiers CAB et
la signature par encodage.

Paquets et/ou Compressi


fichiers on de
supplémentaires fichier CAB Encodage Résultat
Non Non Non Une bibliothèque ActiveX.
Non Non Oui Une bibliothèque ActiveX.
Non Oui Non Un fichier CAB contenant une
bibliothèque ActiveX.
Non Oui Oui Un fichier CAB contenant une
bibliothèque ActiveX.
Oui Non Non Un fichier .INF, une bibliothèque
ActiveX et les fichiers et/ou paquets
supplémentaires.
Oui Non Oui Un fichier .CAB contenant un fichier
.INF, une bibliothèque ActiveX et les
fichiers et/ou paquets supplémentaires.
Oui Oui Non Un fichier .INF, un fichier .CAB contenant
un fichier .OCX et un fichier .CAB pour
chaque fichier et paquet supplémentaire.
Oui Oui Oui Un fichier .CAB contenant un fichier
.INF, un fichier .CAB contenant un
fichier .OCX et un fichier .CAB pour
chaque fichier et paquet supplémentaire.

Page Projet
La page Projet permet de spécifier l’emplacement de fichiers et d’URL et de
définir d’autres options de déploiement pour le projet. Les options de la page
Projet s’appliquent aux fichiers bibliothèque ActiveX ou au fichier CAB contenant
le contrôle ActiveX et devient l’option par défaut pour les paquets et fichiers
supplémentaires déployés avec le projet.

Répertoires et URL Signification


Répertoire destination Emplacement de la bibliothèque ActiveX sur le serveur Web, sous
la forme d’un chemin d’accès. Ce peut être un chemin d’accès
standard ou un chemin d’accès UNC.
Exemple : C:\INETPUB\wwwroot

48-22 Guide du développeur


Déploiement d’un contrôle ActiveX sur le Web

Répertoires et URL Signification


URL destination Emplacement de la bibliothèque ActiveX sur le serveur Web, sous
la forme d’un chemin URL.
Exemple : http://mymachine.inprise.com/
Répertoire HTML Emplacement du fichier HTML contenant une référence à la
bibliothèque ActiveX, sous la forme d’un chemin d’accès. Ce peut
être un chemin d’accès standard ou un chemin d’accès UNC.
Exemple : C:\INETPUB\wwwroot

Remarque Les emplacements spécifiés sont des chemins d’accès stricts ne contenant pas de
nom de fichier.
Outre la spécification de l’emplacement des fichiers ActiveX, la page Projet
permet de spécifier s’il faut utiliser la compression de fichier CAB, les numéros
de version, l’encodage, etc. Le tableau suivant décrit les options proposées :

Options générales Signification


Utiliser la compression de Compresse la bibliothèque ActiveX, et, sauf précision contraire,
fichier CAB les paquets nécessaires et les fichiers supplémentaires. La
compression stocke les fichiers dans une bibliothèque de
fichiers et peut réduire le temps de téléchargement jusqu’à
70 %.
Inclure le n° de version du Inclut le numéro de version spécifié dans Projet|Options
fichier InfoVersion.
Incrémenter le n° de sous- Incrémente automatiquement le numéro de sous-version
version contenu dans Projet|Options InfoVersion.
Encoder le projet Signature par encodage de la bibliothèque ActiveX ou du
fichier CAB contenant la bibliothèque ActiveX, et, sauf
précision contraire, des DLL et fichiers supplémentaires à
déployer. L’encodage identifie l’auteur du contrôle et sa
société. Sa signature digitale garantit aux utilisateurs du
contrôle que ce dernier n’a pas été modifié depuis qu’il a été
encodé.
Déployer les paquets requis Si elle est cochée, déploie les paquets nécessaires du projet.
Déployer les fichiers Si elle est cochée, déploie les fichiers supplémentaires spécifiés
supplémentaires dans l’onglet Fichier supplémentaires avec le projet.

Page Paquets
La page Paquets permet de spécifier comment déployer les paquets utilisés dans
le projet. Chaque paquet utilisé par le projet peut spécifier individuellement ces
paramètres. Quand vous déployez votre contrôle ActiveX, vous pouvez spécifier
des options de déploiement individuellement pour chaque fichier paquet utilisé
par le projet pour le déploiement. Chacun de ces paquets peut être encodé et
placé dans un fichier CAB. Les paquets livrés avec Delphi sont déjà encodés avec
la signature Inprise.

Création d’un contrôle ActiveX 48-23


Déploiement d’un contrôle ActiveX sur le Web

Paquets utilisés par ce projet


Pour modifier les paramètres d’un paquet spécifique, sélectionnez le paquet dans
la boîte liste “Paquets utilisés par ce projet” et modifiez les paramètres souhaités.

Options CAB
Option CAB Signification
Compresser dans un CAB Crée un fichier .CAB distinct pour le paquet. C’est la valeur
séparé par défaut.
Compresser dans le CAB Inclut le paquet dans le fichier CAB du projet.
du projet

Options de sortie
Les options de sortie permettent de spécifier si le paquet comprend des
informations de version et s’il est encodé.

Option de sortie Signification


Utiliser InfoVersion du Obtient les informations de version situées dans la ressource
fichier du paquet et les place dans le fichier .INF du projet.
Encoder le fichier Encode le paquet ou le fichier .CAB contenant le paquet.

Options de répertoire et URL


Option de répertoire et
URL Signification
URL destination (laisser Emplacement du paquet sur le serveur Web, sous la forme
vide si le fichier existe sur d’une URL. Si cette option est laissée vide, la navigateur Web
les machines distantes) suppose que le fichier existe sur la machine distante. Si le
paquet n’est pas trouvé sur la machine distante, le
téléchargement de la bibliothèque ActiveX échoue.
Répertoire destination Emplacement du paquet sur le serveur Web, sous la forme
(laisser vide si le fichier d’un chemin d’accès. Ce peut être un nom de chemin d’accès
existe sur le serveur) standard ou un chemin d’accès UNC. Laissez vide si vous
supposez que le fichier existe et ne doit pas être remplacé.

Page Fichiers supplémentaires


La page Fichiers supplémentaires permet de spécifier les fichiers supplémentaires
(.DLL, .INI, ressources, etc.) devant être déployés avec le contrôle ActiveX.
Pour ajouter un fichier au déploiement ; cliquez sur le bouton Ajouter. Cela
affiche une boîte de dialogue permettant de sélectionner un fichier. Chaque
fichier ainsi ajouté apparaît dans la boîte liste “Fichiers associés au projet”.

48-24 Guide du développeur


Déploiement d’un contrôle ActiveX sur le Web

Fichiers associés au projet


Pour modifier les paramètres d’un fichier donné, sélectionnez le fichier dans la
boîte “Fichiers associés au projet” et modifiez les paramètres souhaités.

Options CAB

Option CAB Signification


Compresser dans un CAB séparé Crée un fichier .CAB distinct pour le fichier. C’est la
valeur par défaut.
Compresser dans le CAB du projet Inclut le fichier dans le fichier CAB du projet.

Options de sortie

Option de sortie Signification


Utiliser InfoVersion du fichier Si le fichier contient une ressource VersonInfo, obtient
les informations de version de la ressource et les place
dans le fichier .INF du projet.
Encoder le fichier Encode le fichier ou le fichier .CAB contenant le
fichier.

Options de répertoire et URL

Option de répertoire et URL Signification


URL destination (laisser vide si le Emplacement du fichier sur le serveur Web, sous la
fichier existe sur les machines forme d’une URL. Si cette option est laissée vide, le
distantes) navigateur Web suppose que le fichier existe sur la
machine distante. Si le paquet n’est pas trouvé, sur la
machine distante, le téléchargement de la bibliothèque
ActiveX échoue.
Répertoire destination (laisser vide Emplacement du fichier sur le serveur Web, sous la
si le fichier existe sur le serveur) forme d’un chemin d’accès. Ce peut être un nom de
chemin d’accès standard ou un chemin d’accès UNC.
Laissez vide si vous supposez que le fichier existe et
ne doit pas être remplacé.

Page Encodage
La Page Encodage permet à l’utilisateur du contrôle de déterminer avec certitude
qui a écrit le contrôle et si le code n’a pas été modifié depuis la signature.
Chaque fichier déployé peut avoir une signature par encodage. L’encodage ne
modifie pas les données ; il crée une signature digitale qui est ajoutée au fichier.
Pour générer une signature digitale, une valeur hachée (appelée également résumé
du message) est tout d’abord créée en utilisant l’Algorithme cryptographique
spécifié. Cette valeur hachée est ensuite signée en utilisant la clé privée.
Pour la clé privée et les fichiers justificatifs, contactez Microsoft Corporation.

Création d’un contrôle ActiveX 48-25


Déploiement d’un contrôle ActiveX sur le Web

Informations requises
Pour pouvoir encoder le contrôle ActiveX, vous devez spécifier une clé privée et
un fichier de certificat justificatif. Spécifiez ces valeurs dans la zone des
informations requises de la page Encodage.

Information requise Signification


Fichier justificatifs Pour obtenir ce fichier, contactez Microsoft.
Clé privée La clé privée qui n’est connue que du propriétaire est utilisée pour
générer la signature. Pour obtenir ce fichier, contactez Microsoft.

Informations facultatives
Outre les informations requises, vous pouvez spécifier des informations facultatives
permettant au client de connaître le nom de l’application et de la société.

Information facultative Signification


Nom de cette application Le nom de l’application tel qu’il doit apparaître dans le
certificat de signature digitale affiché par le navigateur Web.
URL de l’application/société Une URL donnant un lien sur une page Web concernant le
produit ou la société.

Serveur de date de validité


Chaque fournisseur dispose d’un justificatif qu’il utilise lors de l’encodage du
fichier et ce justificatif peut être renouvelé chaque année. Les fournisseurs datent
souvent leurs signatures digitales pour que la validité du justificatif au moment
où le fichier a été encodé puisse être vérifiée. Vous pouvez choisir un serveur de
date de validité qui génère la date de validité pour votre signature digitale

Serveur de date de validité Signification


Défaut Utilise le serveur de date de validité par défaut disponible
sur Internet, Verisign.
Aucun Ne date pas ce contrôle. L’encodage sans date de validité
peut devenir invalide
Personnalisé Spécifie le nom d’un autre serveur de date de validité.
URL Spécifie l’emplacement sur Internet du serveur de date de
validité.

48-26 Guide du développeur


Déploiement d’un contrôle ActiveX sur le Web

Algorithme cryptographique
Choisissez l’un des algorithmes cryptographiques suivants. MD5, la valeur par
défaut, est le plus couramment utilisé. Vous pouvez sélectionner l’un ou l’autre
des algorithmes selon qu’ils sont gérés ou non par le navigateur.

Algorithme
cryptographique Signification
MD5 L’algorithme MD5 a été développé par la société RSA Data
Security. Il génère une valeur hachée codée sur 128 bits.
SHA 1 L’algorithme SHA a été développé par le NIST (National
Institute of Standards and Technology) et par la NSA
(National Security Agency). Il génère une valeur hachée
codée sur 160 bits.

Création d’un contrôle ActiveX 48-27


48-28 Guide du développeur
Chapitre

Utilisation des bibliothèques


Chapter 49
49
de types
Ce chapitre décrit comment créer et modifier des bibliothèques de types en
utilisant l’éditeur de bibliothèques de types Delphi. Les bibliothèques de types
sont des fichiers incluant des informations sur les types de données, les
interfaces, les fonctions membre et les classes d’objet exposés par un contrôle ou
un serveur ActiveX. Les bibliothèques de types permettent d’identifier le type des
objets et des interfaces disponibles sur un serveur ActiveX. Pour une présentation
détaillée de l’utilité et de l’utilisation des bibliothèques de types, voir chapitre 44,
“Présentation des technologies COM”
Quand vous incluez une bibliothèque de types dans une application COM ou
une bibliothèque ActiveX, vous mettez à la disposition d’autres applications et
outils de programmation des informations sur les objets de votre application
COM.
Avec les outils de développement traditionnels, vous créez des bibliothèques de
types en écrivant des scripts utilisant les langages IDL (Interface Definition
Language) ou ODL (Object Description Language), puis en exécutant ce script via
un compilateur. Avec l’éditeur de bibliothèques de types, Delphi automatise ce
processus, simplifiant ainsi la création de vos bibliothèques de types.
Si vous créez l’objet COM, le contrôle ActiveX ou l’objet Automation en utilisant
un expert, l’éditeur de bibliothèques de types génère automatiquement pour
l’objet la syntaxe Pascal. Vous pouvez facilement par la suite améliorer les
informations de types à l’aide de l’éditeur de bibliothèques de types. Les
modifications apportées à la bibliothèque de types à l'aide de l'éditeur de
bibliothèques de types peuvent être automatiquement mises à jour dans l’objet
associé ; sinon, vous pouvez examiner ces changements et vous y opposer, si la
est activée. Voir à la page 49-35 pour plus d’informations.

Utilisation des bibliothèques de types 49-1


L’éditeur de bibliothèques de types

Vous pouvez aussi utiliser l'éditeur de bibliothèques de types Delphi dans le


développement d'applications CORBA (Common Object Request Broker
Architecture). Avec les outils CORBA traditionnels, vous devez définir les
interfaces d'objets en dehors de votre application, à l'aide du langage CORBA
IDL (Interface Definition Language). Vous exécutez ensuite un utilitaire qui
génère du code stub-et-squelette à partir de cette définition. Toutefois, Delphi
génère le stub, le squelette et le code IDL automatiquement. Vous pouvez
facilement modifier votre interface à l'aide de l’éditeur de bibliothèques de
typesDelphi chargeant de mettre automatiquement à jour les fichiers source
correspondants. Pour plus d'informations sur CORBA, voir chapitre 28, “Ecriture
d’applications CORBA.”
Une bibliothèque de types peut contenir les éléments suivants :
• Des informations sur les types de données, dont les alias, les structures et les
unions.
• Les descriptions d’un ou de plusieurs éléments COM, par exemple une
interface, une dispinterface ou une CoClasse. Ces descriptions sont appelées
informations de types.
• Les descriptions des constantes et méthodes définies dans les unités externes.
• Des références aux descriptions de type situées dans d’autres bibliothèques de
types.
Ce chapitre se termine par des informations sur
• Utilisation de la syntaxe Pascal Objet ou IDL
• Création de nouvelles bibliothèques de types
• Déploiement des bibliothèques de types

L’éditeur de bibliothèques de types


L’éditeur de bibliothèques de types est un outil permettant aux développeurs
d’examiner et de créer les informations de type sur les contrôles ActiveX et les
objets COM.
La figure 49.1 montre l’éditeur de bibliothèques de types affichant les
informations de type d’un contrôle bouton ActiveX.

49-2 Guide du développeur


L’éditeur de bibliothèques de types

Figure 49.1 L’éditeur de bibliothèques de types

Barre
d’outils

Pages

Barre d’état

Volet liste des objets

Les principales zones de l’éditeur de bibliothèques de types sont :


• La barre d’outils, utilisée pour ajouter de nouvelles interfaces et des membres
d’interface à la bibliothèque de types.
• Le volet liste des objets, qui affiche tous les objets existant dans la
bibliothèque de types. Quand vous cliquez sur un élement du volet liste des
objets, il affiche les pages adaptées à cet objet.
• La barre d’état, qui affiche les erreurs de syntaxe quand vous essayez
d’ajouter des types illégaux à la bibliothèque de types.
• Les pages, qui affichent des informations sur l’objet sélectionné. Les pages
affichées dépendent du type d’objet sélectionné.
• La fenêtre d’erreur qui affiche les erreurs détectées lors du chargement de la
bibliothèque de types.

Barre d’outils
La barre d’outils de l’éditeur de bibliothèques de types, située en haut, contient
des boutons sur lesquels vous cliquez pour ajouter de nouveaux objets à la
bibliothèque de types.

Utilisation des bibliothèques de types 49-3


L’éditeur de bibliothèques de types

Vous pouvez ajouter les types d’objet suivants à l’aide de la barre d’outils :

Icône Signification
Une bibliothèque de types. Peut être développée pour détailler les informations de
type, y compris les objets et les interfaces.

Une description d’interface.

Une description de dispinterface (interface de répartition).

Une CoClasse.

Une énumération.

Un alias.

Un enregistrement.

Une union.

Un module.

Une méthode de l’interface, de la dispinterface ou un point d’entrée d’un module.

Une fonction propriété Put par valeur.

Une fonction propriété Put par référence.

Une fonction propriété Get.

Une propriété.

Un champ d’un enregistrement ou d’une union.

Une constante d’une énumération ou d’un module.

Les icônes de la boîte de gauche, Interface, Dispatch, CoClass, Enum, Alias,


Record, Union et Module, représentent les objets information de type que vous
pouvez modifier.
Lorsque vous cliquez sur un bouton de la barre d’outils, l’icône de ce type
d’information apparaît en bas du volet liste des objets. Vous pouvez alors
personnaliser ses attributs dans le volet droit. Les pages apparaissant à droite
dépendent du type de l’icône sélectionnée.

49-4 Guide du développeur


L’éditeur de bibliothèques de types

Lorsque vous sélectionnez un objet, l’éditeur de bibliothèques de types affiche les


membres de l’objet valides. Ces membres apparaissent sur la barre d’outils dans
la deuxième boîte. Par exemple, quand vous sélectionnez Interface, l’éditeur de
bibliothèques de types affiche les icônes Méthode et Propriété dans la deuxième
boîte car il est possible d’ajouter des méthodes et des propriétés à une définition
d’interface. Quand vous sélectionnez Enum, l’éditeur de bibliothèques de types
affiche le membre Const qui est le seul membre possible pour une information
de type Enum.
Dans la troisième boîte, vous pouvez choisir de rafraîchir, recenser ou exporter
votre bibliothèque de types, comme décrit dans “Enregistrement et recensement
des informations d’une bibliothèque de types” à la page 49-34.

Volet liste des objets


Le volet liste des objets affiche tous les éléments de la bibliothèque de types en
cours, en commençant par ses interfaces. Chaque interface se développe pour
montrer les propriétés, méthodes et événements spécifiés :
Figure 49.2 Volet liste des objets

Le menu contextuel du volet liste des objets propose les options suivantes :

Nouveau Affiche un menu contenant une liste des objets à ajouter à la bibliothèque
de types. Ce sont les mêmes objets que ceux proposés dans la barre d’outils.
Couper Supprime l’objet sélectionné et le place dans le Presse-papiers Windows.
Copier Place l’objet sélectionné dans le Presse-papiers Windows.
Coller Insère un objet depuis le Presse-papiers Windows en dessous de l’objet
sélectionné.
Supprimer Supprime l’objet sélectionné.
Visualiser erreurs Inverse la visibilité de la fenêtre d’erreur.
Barre d’outils Inverse la visibilité de la barre d’outils.

Utilisation des bibliothèques de types 49-5


L’éditeur de bibliothèques de types

Barre d’état
Lors de la modification ou de l’enregistrement d’une bibliothèque de types, les
erreurs de syntaxe, les erreurs de traductions et les avertissements sont affichés
dans le volet Barre d’état.
Si, par exemple, vous spécifiez un type non géré par l’éditeur de bibliothèques
de types, vous obtiendrez une erreur de syntaxe. Pour une liste complète des
types gérés par l’éditeur de bibliothèques de types, voir “Types autorisés” à la
page 49-24..

Les pages d’informations de type


Quand vous sélectionnez un objet dans le volet liste des objets, les pages des
informations de type qui apparaissent dans l’éditeur de bibliothèques de types
sont celles autorisées pour l’objet sélectionné. Le tableau suivant indique les
pages affichées selon le type d’objet sélectionné dans le volet liste des objets :

Tableau 49.1 Pages de la bibliothèque de types


Objet informations
de type Pages d’informations de type
Bibliothèque de types Attributs, Utilise, Indicateur, Texte
Interface Attributs, Indicateur, Texte
Dispinterface Attributs, Indicateurs, Texte
CoClasse Attributs, Implémente, Indicateurs, Texte
Enumération Attributs, Texte
Alias Attributs, Texte
Enregistrement Attributs, Texte
Union Attributs, Texte
Module Attributs, Texte
Méthode Attributs, Paramètres, Indicateurs, Texte
Constante Attributs, Indicateurs, Texte
Champ Attributs, Indicateurs, Texte

Page Attributs
Tous les éléments d’une bibliothèque de types ont une page d’attributs qui
permet de définir un nom et d’autres attributs spécifiques à l’élément. Si, par
exemple, une interface est sélectionnée, vous pouvez spécifier le GUID et
l’interface parent. Si un champ est sélectionné, vous pouvez spécifier son type.
Les sections suivantes décrivent en détail les attributs de chaque sorte d’élément
d’une bibliothèque de types.
Les attributs communs à tous les éléments de la bibliothèque de types sont ceux
associés à l’aide. Il est fortement recommandé que vous utilisiez les chaînes
d’aide pour décrire les éléments de la bibliothèque de types afin de faciliter son
utilisation par des applications.

49-6 Guide du développeur


Informations de type d’une bibliothèque

L’éditeur de bibliothèques de types gère deux mécanismes d’assistance. Le


mécanisme traditionnel d’aide, où un fichier d’aide Windows standard a été créé
pour la bibliothèque ou, sinon, un fichier d’aide placé dans une DLL distincte
(pour simplifier la localisation).
Les attributs d’aide suivants s’appliquent à tous les éléments
Tableau 49.2 Attributs communs à tous les types
Attribut Signification
Chaîne d’aide Une courte description de l’élément. Utilisé avec le contexte d’aide
pour spécifier l’aide sous la forme d’un fichier d’aide.
Contexte d’aide L’identificateur de contexte d’aide de l’élément, qui identifie la
rubrique d’aide associée à l’élément dans le fichier d’aide.
Contexte de chaîne Pour les DLL d’aide, l’identificateur de contexte d’aide de l’élément
qui identifie la rubrique d’aide associée à cet élément dans le fichier
d’aide. S’utilise avec les DLL de chaîne d’aide pour proposer l’aide
dans une DLL distincte.

Remarque Le fichier d’aide doit être fourni séparément par le développeur.

Page Texte
Tous les éléments des bibliothèques de types disposent d’une page Texte qui
affiche la syntaxe de l’élément. Cette syntaxe est au format IDL (Interface
Definition Language) ou Pascal Objet. Les modifications effectuées dans les
autres pages de l’élément sont reflétées ici. Si vous ajoutez du code directement
dans la page Texte, les modifications sont reflétées dans les autres pages de
l’éditeur de bibliothèques de types.
L’éditeur de bibliothèques de types génère des erreurs de syntaxe si vous ajoutez
des identificateurs qui ne sont pas gérés par l’éditeur ; l’éditeur ne gère
actuellement que les identificateurs associés à la gestion de bibliothèques (et pas
ceux associés à la gestion RPC ou les constructions utilisées par le compilateur
Microsoft IDL pour la génération de code C++ ou la gestion du marshalling).

Pages Indicateurs
Certains éléments d’une bibliothèque de types disposent d’indicateurs qui
permettent d’activer ou d’inhiber certains comportements. Les sections suivantes
décrivent en détail les indicateurs pour chaque sorte d’élément de la bibliothèque
de types.

Informations de type d’une bibliothèque


Quand une bibliothèque de types (le noeud de niveau supérieur) est sélectionnée
dans le volet liste des objets, vous pouvez modifier les informations de type de
la bibliothèque même en modifiant les pages suivantes :
• Page Attributs
• Page Utilise
• Page Indicateurs

Utilisation des bibliothèques de types 49-7


Informations de type d’une bibliothèque

Page Attributs d’une bibliothèque de types


La page Attributs affiche les informations de type pour la bibliothèque de types
sélectionnée :

Tableau 49.3 Attributs d’une bibliothèque de types


Attribut Signification
Nom Nom descriptif de la bibliothèque de types, sans espace ni ponctuation.
GUID Identificateur global unique sur 128 bits de l’interface.
Version Version particulière de la bibliothèque au cas où plusieurs versions existent.
La version est soit une paire d’entiers décimaux séparés par un point, soit
un entier décimal unique. Le premier de ces deux entiers représente le
numéro principal de la version, le second représente le numéro mineur de
la version de l’interface. Si un seul entier est spécifié, il représente le
numéro principal de la version. Ces deux numéros de version sont des
entiers courts non signés compris entre 0 et 65535 (bornes comprises).
LCID L’identificateur local qui décrit la seule langue nationale utilisée pour les
chaînes de texte de la bibliothèque de types et les éléments.
Chaîne d’aide Une courte description de l’élément. Utilisé avec le contexte d’aide pour
spécifier l’aide sous la forme d’un fichier d’aide.
Contexte d’aide L’identificateur de contexte d’aide de l’élément, qui identifie la rubrique
d’aide associée à l’élément dans le fichier d’aide.
Contexte de Pour les DLL d’aide, l’identificateur de contexte d’aide de l’élément qui
chaîne identifie la rubrique d’aide associée à cet élément dans le fichier d’aide.
S’utilise avec les DLL de chaîne d’aide pour proposer l’aide dans une
DLL distincte.
DLL chaîne d’aide Le nom complet de la DLL utilisée pour l’aide.
Fichier d’aide Le nom du fichier d’aide associé à la bibliothèque de types.

Page Utilise d’une bibliothèque de types


La page Utilise fait la liste des noms et des emplacements des autres
bibliothèques de types auxquelles fait référence cette bibliothèque de types.

Page Indicateurs d’une bibliothèque de types


Les indicateurs suivants apparaissent dans la page Indicateurs quand une
bibliothèque de types est sélectionnée. Cette page spécifie comment d’autres
applications doivent utiliser les serveurs associés à cette bibliothèque de types :

Tableau 49.4 Indicateurs d’une bibliothèque de types


Indicateur Signification
Restricted Empêche la bibliothèque d’être utilisée par un programmeur de macros.
Control Indique que la bibliothèque représente un contrôle.
Hidden Indique que la bibliothèque existe mais qu’elle ne doit pas être affichée dans
un navigateur à l’intention des utilisateurs.

49-8 Guide du développeur


Pages d’une interface

Pages d’une interface


L’interface décrit les méthodes (et les propriétés exprimées comme fonctions get/
set) d’un objet auquel il faut d’accéder via une table de fonction virtuelle (VTable).
Si une interface est définie comme double, ce qui est le cas par défaut, une
dispinterface est également impliquée et peut être atteinte via l’automation OLE.
Vous pouvez modifier l’interface d’une bibliothèque de types en :
• Modifiant les attributs
• Changeant les indicateurs
• Ajoutant, supprimant ou modifiant les membres de l’interface

Page Attributs d’une interface


La page Attributs énumère les informations de type suivantes :
Tableau 49.5 Attributs d’une interface
Attribut Signification
Nom Nom descriptif de l’interface.
GUID Identificateur global unique sur 128 bits de l’interface (optionnel).
Version Version particulière de l’interface au cas où plusieurs versions existent. La
version est soit une paire d’entiers décimaux séparés par un point, soit un
entier décimal unique. Le premier de ces deux entiers représente le numéro
principal de la version, le second représente le numéro mineur de la
version de l’interface. Si un seul entier est spécifié, il représente le numéro
principal de la version. Ces deux numéros de version sont des entiers
courts non signés compris entre 0 et 65535 (bornes comprises).
Interface parent Le nom de l’interface de base de cette interface.
Chaîne d’aide Une courte description de l’élément. Utilisé avec le contexte d’aide pour
spécifier l’aide sous la forme d’un fichier d’aide.
Contexte d’aide L’identificateur de contexte d’aide de l’élément, qui identifie la rubrique
d’aide associée à l’élément dans le fichier d’aide.
Contexte de Pour les DLL d’aide, l’identificateur de contexte d’aide de l’élément qui
chaîne identifie la rubrique d’aide associée à cet élément dans le fichier d’aide.
S’utilise avec les DLL de chaîne d’aide pour proposer l’aide dans une DLL
distincte.

Indicateurs d’une interface


Les indicateurs suivants sont autorisés quand une interface est sélectionnée dans
le volet liste des objets.
Tableau 49.6 Indicateurs d’une interface
Indicateur Signification
Hidden Indique que l’interface existe mais ne doit pas être affichée dans un
navigateur à l’intention des utilisateurs.
Dual Identifie une interface qui expose des propriétés et des méthodes via
IDispatch et directement grâce à une v-table.

Utilisation des bibliothèques de types 49-9


Pages d’une interface

Tableau 49.6 Indicateurs d’une interface (suite)


Indicateur Signification
Ole Automation Indique qu’une interface n’utilise que les types compatibles avec
l’Automation. Cet indicateur n’est pas autorisé pour les dispinterfaces
car elles sont, par définition, compatibles avec l’Automation.
Non-extensible Indique que l’interface ne peut s’utiliser comme interface de base d’une
autre interface.

Membres d’une interface


L’éditeur de bibliothèques de types permet de définir ou de modifier :
• Des propriétés
• Des méthodes
Vous pouvez également modifier les paramètres des propriétés et des méthodes
avec la page Paramètres.
L’interface est plus couramment utilisée que la dispinterface pour décrire les
propriétés et méthodes d’un objet.
Les membres des interfaces qui veulent déclencher des exceptions doivent
renvoyer un HRESULT et spécifier un paramètre de valeur renvoyée
(PARAM_RETVAL) pour la valeur réellement renvoyée. Déclarez ces méthodes
en utilisant la convention d’appel safecall.

Méthodes d’une interface


Quand vous sélectionnez l’icône Méthode pour ajouter une nouvelle méthode,
l’éditeur de bibliothèques de types affiche les pages autorisées pour une
méthode : Attributs, Paramètres, Indicateurs et Texte.

Attributs d’une méthode


Les attributs d’une méthode d’interface sont les suivants :

Tableau 49.7 Attributs d’une méthode


Attribut Signification
Nom Nom du membre.
ID Identificateur de répartition.
Genre Indique si la méthode ou la propriété est une fonction. Pour les
méthodes, spécifie la fonction.

Paramètres d’une méthode


Les paramètres des méthodes sont spécifiés de la manière décrite dans “Page
Paramètres des propriétés et méthodes” à la page 49-12.

49-10 Guide du développeur


Pages d’une interface

Indicateurs d’une méthode


Les indicateurs d’une méthode d’interface sont les suivants :

Tableau 49.8 Indicateurs d’une méthode


Identificateur
Indicateur IDL Signification
Replaceable replaceable L’objet gère IConnectionPointWithDefault.
Restricted restricted Empêche la propriété ou la méthode d’être utilisée par
un programmeur.
Source source Indique que le membre renvoie un objet ou un
VARIANT source d’événements.
Bindable bindable Indique que la propriété gère la liaison de données.
Hidden hidden Indique que la propriété existe mais ne doit pas être
affichée dans un navigateur à l’intention des
utilisateurs.
UI Default uidefault Indique que le membre d’information de type est le
membre par défaut dans l’interface utilisateur.

Propriétés d’une interface


Les propriétés des interfaces sont représentées par les méthodes ’get/set’ utilisées
pour lire ou écrire les données sous-jacentes de la propriété. Elles ont une icône
spéciale indiquant leur fonction.
Remarque Les propriétés ActiveX peuvent être marquées en écriture par référence, ce qui
signifie que la propriété est passée en tant que pointeur plutôt que par valeur.
Certaines applications, comme Visual Basic, utilisent si possible l’écriture par
référence pour optimiser les performances. Pour passer une propriété
uniquement par référence plutôt que par valeur, utilisez le type de propriété Par
référence seulement. Pour passer une propriété par référence aussi bien que par
valeur, sélectionnez Lecture | Ecriture | Ecriture par référence. Pour activer ce
menu, sélectionnez dans la barre d'outils la flèche en regard de l'icône de la
propriété.

Attributs d’une propriété


Les attributs d’une propriété d’interface sont les suivants :
Tableau 49.9 Attributs d’une propriété
Attribut Signification
Nom Nom du membre.
ID Identificateur de répartition.
Type Type de propriété ; ce peut être l’un des types spécifiés dans “Types
autorisés” à la page 49-24.
Genre Indique si la propriété est une fonction get, une fonction put ou une
fonction put par référence.

Utilisation des bibliothèques de types 49-11


Pages d’une interface

Indicateurs d’une propriété


Les indicateurs utilisables pour une propriété d’interface sont les suivants :
Tableau 49.10 Indicateurs d’une propriété
Identificateur
Indicateur IDL Signification
Replaceable replaceable L’objet gère IConnectionPointWithDefault.
Restricted restricted Empêche l’utilisation de la propriété ou de la
méthode par un programmeur.
Source source Indique que le membre renvoie un objet ou un
VARIANT source d’événements.
Bindable bindable Indique si la propriété gère la liaison de données.
Request Edit requestedit Indique si la propriété gère la notification
OnRequestEdit.
Display Bindable displaybind Indique si une propriété doit être affichée à
l’utilisateur comme pouvant être liée aux données.
Default Bindable defaultbind Indique une seule propriété, pouvant se lier aux
données, qui représente le mieux l’objet. Les
propriétés ayant l’attribut defaultbind doivent
également avoir l’attribut bindable. Il ne peut y
avoir qu’une seule propriété d’un dispinterface
ayant cet attribut.
Hidden hidden Indique que la propriété existe mais ne doit pas
être affichée dans un navigateur à l’intention des
utilisateurs.
Default Collection defaultcollelem Utilisé pour l’optimisation du code dans Visual
Element Basic.
UI Default uidefault Indique que ce membre est le membre par défaut
dans l’interface utilisateur.
Non Browsable nonbrowsable Indique que la propriété apparaît dans un
navigateur d’objets qui n’affiche pas la valeur des
objets mais n’apparaît pas dans un navigateur de
propriétés affichant la valeur des propriétés.
Immediate Bindable immediatebind Permet à des propriétés d’une fiche de spécifier ce
comportement de manière spécifique. Quand ce bit
est activé, toutes les modifications sont notifiées.
Les bits bindable et requestedit doivent être activés
pour que celui-ci prenne effet.

Page Paramètres des propriétés et méthodes


La page Paramètres permet de spécifier les paramètres et les valeurs renvoyées
par les fonctions.
Pour les fonctions des propriétés, le type de la propriété est dérivé soit du type
renvoyé, soit du dernier paramètre. La modification du type dans la page des
paramètres affecte le type de la propriété affiché dans la page des attributs. De
même, la modification du type affiché dans la page des attributs affecte le
contenu de la page des paramètres.

49-12 Guide du développeur


Pages d’une interface

Remarque La modification d’une fonction de propriété affecte toutes les fonctions de


propriété associées. L’éditeur de bibliothèques de types suppose que les fonctions
de propriétés sont associées si elles ont le même nom ou le même identificateur
de répartition (Dispatch ID).
La page Paramètres est différente selon que vous travaillez en IDL ou en Pascal
Objet.
Quand vous ajoutez un paramètre, si vous travaillez en Pascal Objet, l’éditeur de
bibliothèques de types fournit :
• Le modificateur
• Le nom par défaut
• Le type
Si vous travaillez en IDL, l’éditeur de bibliothèques de types fournit :
• Le nom par défaut
• Le type
• L’indicateur
Vous pouvez modifier le nom en saisissant une nouvelle valeur. Modifiez le type
en choisissant une nouvelle valeur dans la liste déroulante. Les types autorisés
sont ceux gérés par l’éditeur de bibliothèques de types indiqués dans “Types
autorisés” à la page 49-24.
Si vous travaillez en Pascal Objet, changez le modificateur en choisissant une
nouvelle valeur dans la liste déroulante. Les valeurs possibles sont :

Tableau 49.11 Modificateurs de paramètres (syntaxe Pascal Objet)


Modificateur Signification
vide (par défaut) Paramètre d’entrée. Ce peut être un pointeur, mais la valeur qu’il
désigne n’est pas renvoyée. (Identique à in dans IDL).
none Aucune information n’est fournie pour les valeurs des paramètres de
marshaling. Ce modificateur est a employer uniquement avec les
dispinterfaces, qui ne sont pas sujets au marshaling. (Identique à aucun
indicateur dans IDL)
out Paramètre de sortie. C’est une valeur de référence qui reçoit la valeur
renvoyée. (Identique à out dans IDL)
optionalout Paramètre de sortie facultatif. Il doit être du type Variant, et tous les
paramètres suivants sont facultatifs. (Identique à [out, optional] dans
IDL)
var Paramètre d’entrée/sortie. Combinaison de vide et de out. (Identique à
[in, out] dans IDL)
optionalvar Paramètre d’entrée/sortie facultatif. Combinaison de optional et de
optionalout. (Identique à [in, out, optional] dans IDL)
optional Paramètre d’entrée facultatif. Il doit être du type Variant, et tous les
paramètres suivants sont facultatifs. (Identique à [in, optional] dans IDL)
retvak Reçoit la valeur renvoyée. La valeur renvoyée doit être le dernier
paramètre (comme l’impose l’éditeur de bibliothèques de types). Les
paramètres ayant cette valeur ne sont pas affichés dans les navigateurs à
l’intention des utilisateurs. Ne s’applique que si la fonction est déclarée
sans la directive safecall. (Identique à [out, optionalout] dans IDL)

Utilisation des bibliothèques de types 49-13


Informations de type d’une interface de répartition

Utilisez la colonne défaut pour spécifier les valeurs de paramètres par défaut.
Lorsque vous ajoutez une valeur par défaut, l’éditeur de bibliothèques de types
ajoute automatiquement les indicateurs appropriés à la bibliothèque de types.
Pour modifier un indicateur de paramètre (quand vous travaillez en IDL),
double-cliquez sur le champ modificateur afin d’afficher la boîte de dialogue des
indicateurs de paramètres. Vous pouvez sélectionner les indicateurs de paramètre
suivants :
Tableau 49.12 Indicateurs de paramètres (syntaxe IDL)
Indicateur Signification
In Paramètre d’entrée. Ce peut être un pointeur, mais la valeur qu’il désigne
n’est pas renvoyée.
Out Paramètre de sortie. Ce doit être un pointeur sur un membre qui recevra
le résultat.
RetVal Reçoit la valeur renvoyée. Les valeurs renvoyées doivent avoir l’attribut
out et être le dernier paramètre (comme l’impose l’éditeur de bibliothèques
de types). Les paramètres ayant cette valeur ne sont pas affichés dans les
navigateurs à l’intention des utilisateurs.
LCID Indique que ce paramètre est un identificateur local. Un seul paramètre
peut avoir cet attribut. Il doit avoir également l’attribut in et être de type
long. Cela permet aux membres de la VTable de recevoir un LCID au
moment de l’appel. Les paramètres ayant cette valeur ne sont pas affichés
dans les navigateurs à l’intention des utilisateurs. Par convention, LCID est
le paramètre précédant la valeur renvoyée. Interdit dans un dispinterfaces.
Optional Spécifie un paramètre optionnel. Tous les paramètres suivants doivent
également être optionnels.
Has Default Cet indicateur permet de spécifier une valeur par défaut pour un
Value paramètre optionnel typé. La valeur doit être une constante pouvant se
décrire comme un VARIANT.
Default Value Si vous activez l’indicateur A une valeur par défaut, spécifiez ici la valeur
par défaut. La valeur doit être de même type que le paramètre optionnel.

Remarque Lorsque vous travaillez dans IDL, les valeurs par défaut sont spécifiées à l’aide
d’indicateurs plutôt que dans une colonne à part. De même, les identificateurs
locaux sont spécifiés à l’aide d’un indicateur plutôt qu’en utilisant un
spécificateur de type de paramètre de TLCID.
Vous pouvez utiliser les boutons Vers le haut et Vers le bas pour modifier
l’ordre des paramètres. Cependant, l’éditeur n’autorise pas des déplacements qui
violeraient une règle du langage IDL. Ainsi, l’éditeur de bibliothèques de types
applique la règle imposant que la valeur renvoyée soit toujours le dernier
paramètre de la liste des paramètres.

Informations de type d’une interface de répartition


Les interfaces sont plus couramment utilisées que les dispinterfaces pour décrire
les propriétés et méthodes d’un objet. Les dispinterfaces ne sont accessibles que
via une liaison dynamique alors que les interfaces peuvent avoir une liaison
statique via une vtable.

49-14 Guide du développeur


Informations de type d’une interface de répartition

Vous pouvez modifier une interface de répartition (dispinterface) en :


• Modifiant les attributs
• Modifiant les indicateurs
• Ajoutant, supprimant ou modifiant les membres de la dispinterface

Page Attributs d’une interface de répartition


Les attributs suivants s’appliquent à la dispinterface :

Tableau 49.13 Attributs d’une dispinterface


Attribut Signification
Nom Nom sous lequel la dispinterface est connue dans la bibliothèque de types.
Ce doit être un nom unique dans la bibliothèque de types.
GUID Identificateur global unique sur 128 bits de l’interface de répartition
(optionnel).
Version Version particulière de l’interface de répartition au cas où plusieurs versions
existent. La version est soit une paire d’entiers décimaux séparés par un
point, soit un entier décimal unique. Le premier de ces deux entiers
représente le numéro principal de la version, le second représente le
numéro mineur de la version de l’interface. Si un seul entier est spécifié, il
représente le numéro principal de la version. Ces deux numéros de version
sont des entiers courts non signés compris entre 0 et 65535 (bornes
comprises).
Chaîne d’aide Une courte description de l’élément. Utilisé avec le contexte d’aide pour
spécifier l’aide sous la forme d’un fichier d’aide.
Contexte L’identificateur de contexte d’aide de l’élément, qui identifie la rubrique
d’aide d’aide associée à l’élément dans le fichier d’aide.
Contexte de Pour les DLL d’aide, l’identificateur de contexte d’aide de l’élément qui
chaîne identifie la rubrique d’aide associée à cet élément dans le fichier d’aide.
S’utilise avec les DLL de chaîne d’aide pour proposer l’aide dans une DLL
distincte.

Page Indicateurs d’une interface de répartition


La page Indicateurs d’une interface de répartition est identique à celle d’une
interface. Voir “Indicateurs d’une interface” à la page 49-9.

Membres d’une interface de répartition


Dans une interface de répartition (dispinterface) il est possible de définir :
• Des méthodes
• Des propriétés
L’ajout des méthodes et propriétés d’une interface de répartition se fait de la
même manière que pour les interfaces, comme décrit dans “Page Paramètres des
propriétés et méthodes” à la page 49-12.

Utilisation des bibliothèques de types 49-15


Pages d’une CoClasse

Attention, lorsque vous créez une propriété pour une dispinterface, vous ne
pouvez plus spécifier le type des fonctions ou des paramètres. Les indicateurs
des méthodes et propriétés des interfaces de répartition sont les mêmes que ceux
des propriétés et méthodes des interfaces décrits dans “Méthodes d’une
interface” à la page 49-10 et dans “Propriétés d’une interface” à la page 49-11.

Pages d’une CoClasse


Une CoClasse décrit un objet COM qui implémente une ou plusieurs interfaces
et spécifie quelle interface implémentée est celle par défaut pour l’objet et, de
manière optionnelle, quelle dispinterface est la source par défaut des événements.
Vous pouvez modifier une définition de CoClasse de la bibliothèque de types
en :
• Modifiant les attributs
• Modifiant la page Implémente
• Modifiant les indicateurs

Page Attributs d’une CoClasse


Les attributs s’appliquant à une CoClasse sont :

Tableau 49.14 Attributs d’une CoClasse


Attribut Signification
Nom Le nom descriptif de la CoClasse.
GUID L’identificateur global unique sur 128 bits de la CoClasse (optionnel).
Version Version particulière de la CoClasse au cas où plusieurs versions existent.
La version est soit une paire d’entiers décimaux séparés par un point, soit
un entier décimal unique. Le premier de ces deux entiers représente le
numéro principal de la version, le second représente le numéro mineur de
la version de l’interface. Si un seul entier est spécifié, il représente le
numéro principal de la version. Ces deux numéros de version sont des
entiers courts non signés compris entre 0 et 65535 (bornes comprises).
Chaîne d’aide Une courte description de l’élément. Utilisé avec le contexte d’aide pour
spécifier l’aide sous la forme d’un fichier d’aide.
Contexte d’aide L’identificateur de contexte d’aide de l’élément, qui identifie la rubrique
d’aide associée à l’élément dans le fichier d’aide.
Contexte de Pour les DLL d’aide, l’identificateur de contexte d’aide de l’élément qui
chaîne identifie la rubrique d’aide associée à cet élément dans le fichier d’aide.
S’utilise avec les DLL de chaîne d’aide pour proposer l’aide dans une DLL
distincte.

49-16 Guide du développeur


Pages d’une CoClasse

Page Implémente d’une CoClasse


La page Implémente est utilisée pour spécifier les interfaces et dispinterfaces qui
sont implémentées pour la CoClasse. Pour chaque interface, la page Implémente
spécifie si les éléments suivants sont gérés :

Tableau 49.15 Options de la Page Implémente d’une CoClasse


Interface Description
Interface Nom d’une interface implémentée par la CoClasse.
GUID Identifie le GUID de l’interface membre de l’objet.
Source Indique que ce membre est une source d’événements.
Défaut Indique que l’interface ou la dispinterface représente l’interface
programmable par défaut. Une CoClasse peut avoir au maximum deux
membres par défaut. L’un représente l’interface source ou la dispinterface,
et l’autre représente l’interface cible ou la dispinterface.
Restreint Empêche l’utilisation de l’élément par un programmeur. Le membre d’une
interface ne peut avoir simultanément les attributs Restreint et Défaut.
VTable Permet à un objet d’avoir deux interfaces source différentes.

Le menu contextuel de la page Implémente propose des options pour inverser


les options ci-dessus. L’option menu ’Insérer une interface’ ouvre une boîte de
dialogue vous permettant de choisir une interface à ajouter à la CoClasse. La
liste propose les interfaces définies dans la bibliothèque de types en cours et les
interfaces définies dans toutes les bibliothèques de types référencées par la
bibliothèque de types en cours.

Indicateurs d’une CoClasse


Les indicateurs suivants sont autorisés quand une CoClasse est sélectionnée dans
le volet liste des objets.

Tableau 49.16 Indicateurs d’une CoClasse


Indicateur Signification
Hidden Indique que l’interface existe mais ne doit pas être affichée dans un
navigateur à l’intention des utilisateurs.
Can Create Une instance peut être créée avec CoCreateInstance.
Application Object Identifie la CoClasse comme un objet application associé avec une
application EXE et indique que les fonctions et propriétés de la
CoClasse sont utilisables globalement dans la bibliothèque de types.
Licensed Indique que la CoClasse auquel cet indicateur s’applique est soumise à
une licence et doit être instanciée en utilisant IClassFactory2.
Predefined L’application client doit automatiquement créer une seule instance de
l’objet ayant cet attribut.
Control Identifie la CoClasse comme un contrôle ActiveX dont un site conteneur
peut dériver d’autres bibliothèques de types ou d’autres CoClasses.

Utilisation des bibliothèques de types 49-17


Informations de type d’une énumération

Tableau 49.16 Indicateurs d’une CoClasse (suite)


Indicateur Signification
Aggregatable Indique que les membres de cette classe peuvent être récapitulés.
Replaceable L’objet gère IConnectionPointWithDefault.

Informations de type d’une énumération


Vous pouvez ajouter ou modifier une définition d‘énumération (Enum) de la
bibliothèque de types en :
• Modifiant les attributs
• Ajoutant, supprimant ou modifiant les membres de l’énumération

Page Attributs d’une énumération


Les attributs applicables à une énumération sont les suivants :
Tableau 49.17 Attributs d’une énumération
Attribut Signification
Nom Le nom descriptif de l’énumération.
GUID L’identificateur global unique sur 128 bits de l’énumération (optionnel).
Version Version particulière du type au cas où plusieurs versions de l’énumération
existent. La version est soit une paire d’entiers décimaux séparés par un
point, soit un entier décimal unique. Le premier de ces deux entiers
représente le numéro principal de la version, le second représente le numéro
mineur de la version de l’interface. Si un seul entier est spécifié, il représente
le numéro principal de la version. Ces deux numéros de version sont des
entiers courts non signés compris entre 0 et 65535 (bornes comprises).
Chaîne d’aide Une courte description de l’élément. Utilisé avec le contexte d’aide pour
spécifier l’aide sous la forme d’un fichier d’aide.
Contexte L’identificateur de contexte d’aide de l’élément, qui identifie la rubrique
d’aide d’aide associée à l’élément dans le fichier d’aide.
Contexte de Pour les DLL d’aide, l’identificateur de contexte d’aide de l’élément qui
chaîne identifie la rubrique d’aide associée à cet élément dans le fichier d’aide.
S’utilise avec les DLL de chaîne d’aide pour proposer l’aide dans une DLL
distincte.

Il est fortement conseillé de spécifier une chaîne d’aide pour les énumérations.
Voici un exemple d’entrée d’un type énumération pour un bouton de souris qui
inclut une chaîne d’aide pour chacun des éléments de l’énumération :
mbLeft = 0 [helpstring ‘mbLeft’];
mbRight = 1 [helpstring ‘mbRight’];
mbMiddle = 3 [helpstring ‘mbMiddle’];

49-18 Guide du développeur


Informations de type d’un alias

Membres d’une énumération


Une énumération est composée d’une liste de constantes numériques. Il s’agit
généralement d’entiers au format décimal ou hexadécimal. Par défaut, la valeur
de base est zéro.
Pour définir les constantes d’une énumération, cliquez sur le bouton Nouvelle
constante.

Informations de type d’un alias


Un alias définit un alias (une définition de type) pour un type. Un alias permet
de définir des types s’utilisant dans d’autres types, par exemple des
enregistrements ou des unions.
Vous pouvez créer ou modifier une définition d’alias de la bibliothèque de types
en :
• Modifiant les attributs

Page Attributs d’un alias


La page Attributs d’un alias propose les éléments suivants :

Tableau 49.18 Attributs d’un alias


Attribut Signification
Nom Le nom descriptif sous lequel l’alias est connu dans la bibliothèque de
types.
GUID L’identificateur global unique sur 128 bits de l’interface (optionnel). Si cet
attribut est omis, l’alias n’est pas spécifié de manière unique dans le système.
Version Version particulière de l’alias au cas où plusieurs versions existent. La
version est soit une paire d’entiers décimaux séparés par un point, soit un
entier décimal unique. Le premier de ces deux entiers représente le numéro
principal de la version, le second représente le numéro mineur de la
version de l’interface. Si un seul entier est spécifié, il représente le numéro
principal de la version. Ces deux numéros de version sont des entiers
courts non signés compris entre 0 et 65535 (bornes comprises).
Type Type pour lequel vous voulez un alias.
Chaîne d’aide Une courte description de l’élément. Utilisé avec le contexte d’aide pour
spécifier l’aide sous la forme d’un fichier d’aide.
Contexte L’identificateur de contexte d’aide de l’élément, qui identifie la rubrique
d’aide d’aide associée à l’élément dans le fichier d’aide.
Contexte de Pour les DLL d’aide, l’identificateur de contexte d’aide de l’élément qui
chaîne identifie la rubrique d’aide associée à cet élément dans le fichier d’aide.
S’utilise avec les DLL de chaîne d’aide pour proposer l’aide dans une DLL
distincte.

Utilisation des bibliothèques de types 49-19


Informations de type d’un enregistrement

Informations de type d’un enregistrement


Vous pouvez ajouter ou modifier une définition d’enregistrement d’une
bibliothèque de types en :
• Modifiant les attributs
• Ajoutant, supprimant ou modifiant les membres de l’enregistrement

Page Attributs d’un enregistrement


La page Attributs d’un enregistrement contient :

Tableau 49.19 Attributs d’un enregistrement


Attribut Signification
Nom Nom descriptif sous lequel l’enregistrement est connu dans la
bibliothèque de types.
GUID Identificateur global unique sur 128 bits de l’interface (optionnel). S’il est
omis, l’enregistrement n’est pas spécifié de façon unique dans le système
Version Version particulière de l’enregistrement au cas où plusieurs versions
existent. La version est soit une paire d’entiers décimaux séparés par un
point, soit un entier décimal unique. Le premier de ces deux entiers
représente le numéro principal de la version, le second représente le
numéro mineur de la version de l’interface. Si un seul entier est spécifié,
il représente le numéro principal de la version. Ces deux numéros de
version sont des entiers courts non signés compris entre 0 et 65535
(bornes comprises).
Chaîne d’aide Une courte description de l’élément. Utilisé avec le contexte d’aide pour
spécifier l’aide sous la forme d’un fichier d’aide.
Contexte d’aide L’identificateur de contexte d’aide de l’élément, qui identifie la rubrique
d’aide associée à l’élément dans le fichier d’aide.
Contexte de Pour les DLL d’aide, l’identificateur de contexte d’aide de l’élément qui
chaîne identifie la rubrique d’aide associée à cet élément dans le fichier d’aide.
S’utilise avec les DLL de chaîne d’aide pour proposer l’aide dans une
DLL distincte.

Membres d’un enregistrement


Un enregistrement est constitué de la liste des membres de la structure, ou
champs. Un champ est défini par
• Son nom
• Son type
Les membres peuvent être de n’importe quel type prédéfini. Vous pouvez aussi
spécifier un type en utilisant un alias avant de définir l’enregistrement.
Les enregistrements peuvent être définis avec un repère optionnel.

49-20 Guide du développeur


Informations de type d’une union

Informations de type d’une union


Une union est un enregistrement ayant seulement une partie de type variant.
Vous pouvez ajouter ou modifier une définition d’union de la bibliothèque de
types en :
• Modifiant les attributs
• Ajoutant, supprimant ou modifiant les membres de l’union

Page Attributs d’une union


La page Attributs d’une union propose les attributs suivants :

Tableau 49.20 Attributs d’une union


Attribut Signification
Nom Le nom descriptif sous lequel l’union est connue dans la bibliothèque de types.
GUID L’identificateur global unique sur 128 bits de l’interface (optionnel). Si cet
attribut est omis, l’union n’est pas spécifiée de manière unique dans le système.
Version Version particulière de l’union au cas où plusieurs versions existent. La
version est soit une paire d’entiers décimaux séparés par un point, soit un
entier décimal unique. Le premier de ces deux entiers représente le numéro
principal de la version, le second représente le numéro mineur de la version
de l’interface. Si un seul entier est spécifié, il représente le numéro principal
de la version. Ces deux numéros de version sont des entiers courts non
signés compris entre 0 et 65535 (bornes comprises).
Chaîne d’aide Une courte description de l’élément. Utilisé avec le contexte d’aide pour
spécifier l’aide sous la forme d’un fichier d’aide.
Contexte L’identificateur de contexte d’aide de l’élément, qui identifie la rubrique
d’aide d’aide associée à l’élément dans le fichier d’aide.
Contexte de Pour les DLL d’aide, l’identificateur de contexte d’aide de l’élément qui
chaîne identifie la rubrique d’aide associée à cet élément dans le fichier d’aide. S’utilise
avec les DLL de chaîne d’aide pour proposer l’aide dans une DLL distincte.

Membres d’une union


Comme un enregistrement, une union est composée d’une liste de membres de
structure, des champs. Un champ est composé de :
• Son nom
• Son type
Les membres peuvent être de tout type prédéfini, vous pouvez aussi spécifier un
type en utilisant un alias avant de définir l’enregistrement.
Les unions peuvent être définies avec un repère optionnel.

Utilisation des bibliothèques de types 49-21


Informations de type d’un module

Informations de type d’un module


Un module définit un groupe de fonctions, généralement un ensemble de points
d’entrée de DLL.
Vous pouvez créer ou modifier une définition de module de la bibliothèque de
types en :
• Modifiant les attributs
• Ajoutant, supprimant ou modifiant les membres du module
Remarque Delphi ne génère pas automatiquement les déclarations ou les implémentations
liées au module. La DLL spécifiée est créée par l’utilisateur dans un projet
distinct.

Page Attributs d’un module


La page Attributs d’un module propose les attributs suivants :

Tableau 49.21 Attributs d’un module


Attribut Signification
Nom Le nom descriptif du module.
GUID L’identificateur global unique sur 128 bits de l’interface (optionnel). Si cet
attribut est omis, le module n’est pas spécifié de manière unique dans le
système.
Version Version particulière du module au cas où plusieurs versions existent. La
version est soit une paire d’entiers décimaux séparés par un point, soit un
entier décimal unique. Le premier de ces deux entiers représente le numéro
principal de la version, le second représente le numéro mineur de la
version de l’interface. Si un seul entier est spécifié, il représente le numéro
principal de la version. Ces deux numéros de version sont des entiers
courts non signés compris entre 0 et 65535 (bornes comprises).
Nom de DLL Nom de la DLL associée à laquelle ces points d’entrée s’appliquent.
Chaîne d’aide Une courte description de l’élément. Utilisé avec le contexte d’aide pour
spécifier l’aide sous la forme d’un fichier d’aide.
Contexte L’identificateur de contexte d’aide de l’élément, qui identifie la rubrique
d’aide d’aide associée à l’élément dans le fichier d’aide.
Contexte de Pour les DLL d’aide, l’identificateur de contexte d’aide de l’élément qui
chaîne identifie la rubrique d’aide associée à cet élément dans le fichier d’aide.
S’utilise avec les DLL de chaîne d’aide pour proposer l’aide dans une DLL
distincte.

Membres d’un module


Un module peut être composé :
• De méthodes
• De constantes

49-22 Guide du développeur


Création de nouvelles bibliothèques de types

Méthodes d’un module


Les méthodes d’un module ont les attributs suivants :

Tableau 49.22 Attribut d’une méthode de module


Attribut Signification
Nom Le nom descriptif du module.
Entrée de DLL Le point d’entrée dans la DLL associée.

Les paramètres des méthodes de module se spécifient comme ceux des méthodes
d’interface, comme décrit dans “Page Paramètres des propriétés et méthodes” à
la page 49-12.

Constantes de module
Pour définir une constante de module, il faut spécifier :
• Son nom
• Sa valeur
• Son type
Une constante de module peut être numérique ou chaîne, selon son attribut. Les
entrées numériques sont généralement sous la forme d’un entier au format
décimal ou hexadécimal ; ce peut être aussi une constante caractère (comme \0).
Les entrées chaîne sont délimitées par des guillemets (““) et ne peuvent s’étendre
sur plusieurs lignes. La barre oblique inverse sert de caractère d’échappement.
Quand ce caractère est suivi d’un autre caractère (y compris une autre barre
oblique inverse), il empêche l’interprétation littérale de ce caractère. Par exemple,
pour placer une barre oblique inverse dans un texte, utilisez :
“Pathname: c:\\bin\\”

Création de nouvelles bibliothèques de types


L’éditeur de bibliothèques de types permet de créer des bibliothèques de types
pour des contrôles ActiveX, des serveurs ActiveX et d’autres objets COM.
L’éditeur supporte un sous-ensemble de types autorisés et de SafeArray dans
une bibliothèque de types comme décrit ci-après.
Cette section décrit les opérations suivantes :
• Création d’une nouvelle bibliothèque de types
• Ouverture d’une bibliothèque de types existante
• Ajout d’une interface à une bibliothèque de types
• Ajout de propriétés et de méthodes à une bibliothèque de types
• Ajout d’une CoClasse à une bibliothèque de types
• Ajout d’une énumération à une bibliothèque de types
• Enregistrement et recensement des informations d’une bibliothèque de types

Utilisation des bibliothèques de types 49-23


Création de nouvelles bibliothèques de types

Types autorisés
Dans l’éditeur de bibliothèques de types, vous utilisez des identificateurs de
types différents, selon que vous travaillez en IDL ou en Pascal Objet. Spécifiez le
langage que vous voulez utiliser dans la boîte de dialogue Options
d’environnement.
Les types suivants sont autorisés dans une bibliothèque de types pour le
développement COM. La colonne Compatible Automation spécifie si le type peut
être utilisé par une interface dont l’indicateur Automation ou Dispinterface est
activé.
Tableau 49.23 Types autorisés
Type Compatible
Pascal Type IDL Type de variant Automation Description
Smallint short VT_I2 Oui entier signé sur 2 octets
Integer long VT_I4 Oui entier signé sur 4 octets
Single single VT_R4 Oui Réel sur 4 octets
Double double VT_R8 Oui Réel sur 8 octets
Currency CURRENCY VT_CY Oui Monétaire
TDateTime DATE VT_DATE Oui Date
WideString BSTR VT_BSTR Oui Chaîne binaire
IDispatch IDispatch VT_DISPATCH Oui Pointeur sur une interface
IDispatch
SCODE SCODE VT_ERROR Oui Code d’erreur OLE
WordBool VARIANT_BOOL VT_BOOL Oui true = –1, false = 0
OleVariant VARIANT VT_VARIANT Oui Pointeur sur un Variant OLE
IUnknown IUnknown VT_UNKNOWN Oui Pointeur sur l’interface
IUnknown
Shortint byte VT_I1 Non Entier signé sur 1 octet
Byte unsigned char VT_UI1 Oui Entier non signé sur 1 octet
Word unsigned short VT_UI2 Non* Entier non signé sur 2 octets
UINT unsigned long VT_UI4 Non* Entier non signé sur 4 octets
Int64 __int64 VT_I8 Non Réel signé sur 8 octets
Largeuint uint64 VT_UI8 Non Réel non signé sur 8 octets
SYSINT int VT_INT Non* Entier dépendant du système
(Win32=Integer)
SYSUINT unsigned int VT_UINT Non* Entier non signé dépendant
du système
HResult HRESULT VT_HRESULT Non Code d’erreur 32 bits
Pointer VT_PTR -> VT_VOID Non Pointeur non typé
SafeArray SAFEARRAY VT_SAFEARRAY Non Tableau protégé OLE
PChar LPSTR VT_LPSTR Non Pointeur sur un Char
PWideChar LPWSTR VT_LPWSTR Non Pointeur sur un WideChar
* Word, UINT, SYSINT et SYSUINT peuvent être compatibles avec l’Automation dans certaines
applications.

49-24 Guide du développeur


Création de nouvelles bibliothèques de types

Remarque Pour connaître les types corrects pour le développement CORBA, voir
chapitre 28, “Ecriture d’applications CORBA.”
Remarque Byte (VT_UI1) est compatible avec l’Automation, mais il n’est pas autorisé dans
un Variant ou un OleVariant car de nombreux serveurs Automation ne gèrent
pas correctement cette valeur.
Outre ces types, toute interface ou type défini dans la bibliothèque ou dans les
bibliothèques référencées peut s’utiliser dans une définition de bibliothèque de
types.
L’éditeur de bibliothèques de types stocke les informations de type en utilisant la
syntaxe IDL (Interface Definition Language) dans le fichier (.TLB) de bibliothèque
de types générée sous forme binaire.
Si le type de paramètre est précédé d’un type Pointer, l’éditeur de bibliothèques
de types le convertit en paramètre variable. Quand la bibliothèque de types est
enregistrée, les indicateurs IDL des ElemDesc associés aux paramètres variable
sont marqués IDL_FIN ou IDL_FOUT.
Souvent, les indicateurs IDL ElemDesc ne sont pas marqués par IDL_FIN ni par
IDL_FOUT lorsque le type est précédé d’un pointeur. De même, s’il s’agit de
dispinterfaces, les indicateurs IDL ne sont généralement pas utilisés. Dans ces
situations, on peut voir à côté de l’identificateur de variable un commentaire comme
{IDL_None} ou {IDL_In}. Ces commentaires sont utilisés lors de l’enregistrement
d’une bibliothèque de types, pour marquer correctement les indicateurs IDL.

Les SafeArray
COM requiert que les tableaux soient transmis via un type de données spécial, connu
sous le nom de SafeArray. Vous pouvez créer et détruire les SafeArrays en appelant
des fonctions COM particulières, et tous les éléments d’un SafeArray doivent être
types compatibles automation-valides. Le compilateur Delphi dispose
d’informations intégrées sur les SafeArrays COM et appellera automatiquement
l’API COM pour créer, copier et détruire des SafeArrays.
Dans l’éditeur de bibliothèques de types, un SafeArray doit spécifier son type de
composant. Par exemple, dans le code suivant, le SafeArray spécifie un
composant de type entier :
procedure HighLightLines(Lines: SafeArray of Integer);
Le type de composant d’un SafeArray doit être compatible avec l’Automation.
Dans l’unité de conversion de bibliothèque de types Pascal Objet, le type de
composant n’est ni nécessaire, ni autorisé.

Utilisation de la syntaxe Pascal Objet ou IDL


Par défaut, la page Texte de l’éditeur de bibliothèques de types affiche vos
informations de type en utilisant une extension de la syntaxe Pascal Objet. Mais,
si vous voulez travailler en IDL, il suffit de changer un paramètre du dialogue
Options d’environnement. Choisissez Outils|Options d’environnement, et spécifiez
IDL comme langage de l’éditeur dans la page Bibliothèques de types du dialogue.

Utilisation des bibliothèques de types 49-25


Création de nouvelles bibliothèques de types

Remarque Le choix de la syntaxe Pascal Objet ou IDL affecte également les choix
disponibles dans la page Attributs des paramètres.
Comme c’est généralement le cas pour les applications Pascal Objet, les
identificateurs des bibliothèques de types ne distinguent pas les majuscules des
minuscules. Ils peuvent avoir jusqu’à 255 caractères et doivent commencer par
une lettre ou un caractère de soulignement (_).

Spécifications des attributs


Pascal Objet a été étendu pour que les bibliothèques de types puissent contenir
des spécifications d’attributs. Les spécifications d’attributs sont entourées de
crochets droits et sont séparées par des virgules. Chaque spécification d’attribut
est constituée d’un nom d’attribut suivi (le cas échéant) d’une valeur.
Le tableau suivant est la liste des noms d’attributs et des valeurs
correspondantes.

Tableau 49.24 Syntaxe des attributs


Nom d’attribut Exemple S’applique à
aggregatable [aggregatable] typeinfo
appobject [appobject] typeinfo d’une CoClasse
bindable [bindable] membres, sauf membres d’une
CoClasse
control [control] bibliothèque de types, typeinfo
custom [custom '{7B5687A1-F4E9-11D1- tout
92A8-00C04F8C8FC4}' 0]
default [default] membres d’une CoClasse
defaultbind [defaultbind] membres, sauf membres d’une
CoClasse
defaultcollection [defaultcollection] membres, sauf membres d’une
CoClasse
defaultvtbl [defaultvtbl] membres d’une CoClasse
dispid [dispid] membres, sauf membres d’une
CoClasse
displaybind [displaybind] membres, sauf membres d’une
CoClasse
dllname [dllname 'Helper.dll'] typeinfo d’un module
dual [dual] typeinfo d’une interface
helpfile [helpfile 'c:\help\myhelp.hlp'] bibliothèque de types
helpstringdll [helpstringdll 'c:\help\myhelp.dll'] bibliothèque de types
helpcontext [helpcontext 2005] tout sauf membres et paramètres
d’une CoClasse
helpstring [helpstring 'Interface Paye'] tout sauf membres et paramètres
d’une CoClasse
helpstringcontext [helpstringcontext $17] tout sauf membres et paramètres
d’une CoClasse
hidden [hidden] tout sauf paramètres

49-26 Guide du développeur


Création de nouvelles bibliothèques de types

Tableau 49.24 Syntaxe des attributs (suite)


Nom d’attribut Exemple S’applique à
immediatebind [immediatebind] membres, sauf membres d’une
CoClasse
lcid [lcid $324] bibliothèque de types
licensed [licensed] bibliothèque de types, typeinfo d’une
CoClasse
nonbrowsable [nonbrowsable] membres, sauf membres d’une
CoClasse
nonextensible [nonextensible] typeinfo d’une interface
oleautomation [oleautomation] typeinfo d’une interface
predeclid [predeclid] typeinfo
propget [propget] membres, sauf membres d’une
CoClasse
propput [propput] membres, sauf membres d’une
CoClasse
propputref [propputref] membres, sauf membres d’une
CoClasse
public [public] typeinfo d’un alias
readonly [readonly] membres, sauf membres d’une
CoClasse
replaceable [replaceable] tout sauf membres et paramètres
d’une CoClasse
requestedit [requestedit] membres, sauf membres d’une
CoClasse
restricted [restricted] tout sauf paramètres
source [source] tous les membres
uidefault [uidefault] membres, sauf membres d’une
CoClasse
usesgetlasterror [usesgetlasterror] membres, sauf membres d’une
CoClasse
uuid [uuid '{7B5687A1-F4E9-11D1-92A8- bibliothèque de types, typeinfo
00C04F8C8FC4}' ] (obligatoire)
vararg [vararg] membres, sauf membres d’une
CoClasse
version [version 1.1] bibliothèque de types, typeinfo

Syntaxe pour une interface


La syntaxe Pascal Objet pour déclarer les informations de type d’une interface
est de la forme
interfacename = interface [(baseinterface)] [attributes]
functionlist
[propertymethodlist]
end;

Utilisation des bibliothèques de types 49-27


Création de nouvelles bibliothèques de types

Par exemple, le texte suivant est la déclaration d’une interface avec deux
méthodes et une propriété :
Interface1 = interface (IDispatch)
[uuid '{7B5687A1-F4E9-11D1-92A8-00C04F8C8FC4}', version 1.0]
function Calculate(optional seed:Integer=0): Integer;
procedure Reset;
procedure PutRange(Range: Integer) [propput, dispid $00000005]; stdcall;
function GetRange: Integer;[propget, dispid $00000005]; stdcall;
end;
La syntaxe IDL correspondante est
[uuid ‘{5FD36EEF-70E5-11D1-AA62-00C04FB16F42}’,version 1.0]
interface Interface1 :IDispatch
{
long Calculate([in, optional, defaultvalue(0) ] long seed);
void Reset(void);
[propput, id(0x00000005)] void _stdcall PutRange([in] long Value);
[propput, id(0x00000005)] void _stdcall getRange([out, retval] long *Value);
};

Syntaxe pour une interface de répartition


La syntaxe Pascal Objet pour déclarer les informations de type d’une
dispinterface est de la forme :
dispinterfacename = dispinterface [attributes]
functionlist
[propertylist]
end;
Par exemple, le texte suivant est la déclaration d’une dispinterface avec les
mêmes méthodes et la même propriété que pour l’interface précédente :
MyDispObj = dispinterface
[uuid ‘{5FD36EEF-70E5-11D1-AA62-00C04FB16F42}’,
version 1.0,
helpstring ‘interface de répartition pour MyObj’]
function Calculate(seed:Integer): Integer [dispid 1];
procedure Reset [dispid 2];
property Range: Integer [dispid 3];
end;
La syntaxe IDL correspondante est
[uuid ‘{5FD36EEF-70E5-11D1-AA62-00C04FB16F42}’,
version 1.0,
helpstring “interface de répartition pour MyObj”]
dispinterface Interface1
{
methods:
[id(1)] int Calculate([in] int seed);
[id(2)] void Reset(void);
properties :
[id(3)] int Value;
};

49-28 Guide du développeur


Création de nouvelles bibliothèques de types

Syntaxe pour une CoClasse


La syntaxe Pascal Objet pour déclarer les informations de type d’une CoClasse
est de la forme :
classname = coclass(interfacename[interfaceattributes], ...); [attributes];
Par exemple, le texte suivant est la déclaration d’une coclasse pour l’interface
IMyInt et la dispinterface DmyInt :
myapp = coclass(IMyInt [source], DMyInt);
[uuid ‘{2MD36ABF-90E3-11D1-AA75-02C04FB73F42}’,
version 1.0,
helpstring ‘Une classe’,
appobject]
La syntaxe IDL correspondante est :
[uuid ‘{2MD36ABF-90E3-11D1-AA75-02C04FB73F42}’,
version 1.0,
helpstring ‘Une classe’,
appobject]
coclass myapp
{
methods:
[source] interface IMyInt);
dispinterface DMyInt;
};

Syntaxe pour une énumération


La syntaxe Pascal Objet pour déclarer les informations de type d’une
énumération est de la forme :
enumname = ([attributes] enumlist);
Par exemple, le texte suivant est la déclaration d’un type énuméré avec trois
valeurs :
location = ([uuid ‘{2MD36ABF-90E3-11D1-AA75-02C04FB73F42}’,
helpstring ‘emplacement de la cabine’]
Inside = 1 [helpstring ‘Dans le pavillon’];
Outside = 2 [helpstring ‘A l’extérieur du pavillon’];
Offsite = 3 [helpstring ‘Loin du pavillon’];);
La syntaxe IDL correspondante est
[uuid ‘{2MD36ABF-90E3-11D1-AA75-02C04FB73F42}’,
helpstring ‘emplacement de la cabine’]
typedef enum
{
[helpstring ‘Dans le pavillon’] Inside = 1,
[helpstring ‘A l’extérieur du pavillon’] Outside = 2,
[helpstring ‘Loin du pavillon’] Offsite = 3
} location;

Utilisation des bibliothèques de types 49-29


Création de nouvelles bibliothèques de types

Syntaxe pour un alias


La syntaxe Pascal Objet pour déclarer les informations de type d’un alias est de
la forme :
aliasname = basetype[attributes];
Par exemple, le texte suivant est la déclaration d’un DWORD comme alias d’un
entier :
DWORD = Integer [uuid ‘{2MD36ABF-90E3-11D1-AA75-02C04FB73F42}’];
La syntaxe IDL correspondante est
[uuid ‘{2MD36ABF-90E3-11D1-AA75-02C04FB73F42}’] typedef long DWORD;

Syntaxe pour un enregistrement


La syntaxe Pascal Objet pour déclarer les informations de type d’un
enregistrement est de la forme :
recordname = record [attributes] fieldlist end;
Par exemple, le texte suivant est la déclaration d’un enregistrement :
Tasks = record [uuid ‘{2MD36ABF-90E3-11D1-AA75-02C04FB73F42}’,
helpstring ‘Description de la tâche’]
ID: Integer;
StartDate: TDate;
EndDate: TDate;
Ownername: WideString;
Subtasks: safearray of Integer;
end;
La syntaxe IDL correspondante est :
[uuid ‘{2MD36ABF-90E3-11D1-AA75-02C04FB73F42}’,
helpstring ‘Description de la tâche’]
typedef struct
{
long ID;
DATE StartDate;
DATE EndDate;
BSTR Ownername;
SAFEARRAY (int) Subtasks;
} Tasks;

Syntaxe pour une union


La syntaxe Pascal Objet pour déclarer les informations de type d’une union est
de la forme :
unionname = record [attributes]
case Integer of
0: field1;
1: field2;
...
end;

49-30 Guide du développeur


Création de nouvelles bibliothèques de types

Par exemple, le texte suivant est la déclaration d’une union :


MyUnion = record [uuid ‘{2MD36ABF-90E3-11D1-AA75-02C04FB73F42}’,
helpstring ‘description d’un élément’]
case Integer of
0: (Name: WideString);
1: (ID: Integer);
3: (Value: Double);
end;
La syntaxe IDL correspondante est
[uuid ‘{2MD36ABF-90E3-11D1-AA75-02C04FB73F42}’,
helpstring ‘description d’un élément’]
typedef union
{
BSTR Name;
long ID;
double Value;
} MyUnion;

Syntaxe pour un module


La syntaxe Pascal Objet pour déclarer les informations de type d’un module est
de la forme :
modulename = module constants entrypoints end;
Par exemple, le texte suivant est la déclaration des informations de type d’un
module :
MyModule = module [uuid ‘{2MD36ABF-90E3-11D1-AA75-02C04FB73F42}’,
dllname ‘circle.dll’]
PI: Double = 3.14159;
function area(radius: Double): Double [ entry 1 ]; stdcall;
function circumference(radius: Double): Double [ entry 2 ]; stdcall;
end;
La syntaxe IDL correspondante est
[uuid ‘{2MD36ABF-90E3-11D1-AA75-02C04FB73F42}’,
dllname(“circle.dll”)]
module MyModule
{
double PI = 3.14159;
[entry(1)] double _stdcall area([in] double radius);
[entry(2)] double _stdcall circumference([in] double radius);
};

Création d’une nouvelle bibliothèque de types


Vous pouvez souhaiter créer une bibliothèque de types qui soit indépendante
d’un contrôle ActiveX, par exemple pour définir la bibliothèque de types d’un
contrôle ActiveX qui n’est pas encore implémenté.

Utilisation des bibliothèques de types 49-31


Création de nouvelles bibliothèques de types

Pour créer une nouvelle bibliothèque de types,


1 Choisissez Fichier|Nouveau afin d’ouvrir la boîte de dialogue Nouveaux
éléments.
2 Choisissez la page ActiveX.
3 Sélectionnez l’icône Bibliothèque de types.
4 Choisissez OK.
L’éditeur de bibliothèques de types s’affiche en demandant le nom de la
bibliothèque de types.
5 Entrez le nom de la bibliothèque de types.

Ouverture d’une bibliothèque de types existante


Si vous utilisez les experts pour créer un contrôle ActiveX, un objet Automation,
une fiche active, un objet MTS, un module de données distant ou un module de
données MTS, une bibliothèque de types est automatiquement créée avec une
unité d’implémentation.
Pour ouvrir une bibliothèque de types existante indépendant d’un projet,
1 Choisissez Fichier|Ouvrir afin d’afficher la boîte de dialogue Ouvrir.
2 Dans la liste Type, choisissez Bibliothèques de types.
3 Sélectionnez la bibliothèque de types souhaitée.
4 Choisissez Ouvrir.
Pour ouvrir une bibliothèque de types associée au projet en cours,
1 Choisissez Projet| Bibliothèque de types
Vous pouvez maintenant ajouter des interfaces, CoClasses et d’autres éléments à
la bibliothèque de types (énumérations, propriétés ou méthodes, etc.).
Remarque Les modifications effectuées aux informations de bibliothèque de types à l’aide
de l’éditeur de bibliothèque de types peuvent se refléter automatiquement dans
le contrôle ActiveX associé. Si vous préférez au préalable revoir les
modifications, vérifiez que la boîte de dialogue Appliquer les mises à jour est
activée. Elle est activée par défaut et ce paramétrage peut être modifié avec
l’option d’affichage des mises à jour avant le rafraîchissement présente sur la
page Outils|Options d’environnement|Bibliothèque de types. Pour plus
d’informations, voir “Boîte de dialogue Appliquer les mises à jour,” à la
page 49-35.

Ajout d’une interface à une bibliothèque de types


Pour ajouter une interface,
1 Dans la barre d’outils, cliquez sur l’icône Interface.
Une interface est ajoutée dans le volet liste des objets et vous pouvez en saisir
le nom.

49-32 Guide du développeur


Création de nouvelles bibliothèques de types

2 Entrez le nom de l’interface.


La nouvelle interface a les attributs par défaut que vous pouvez modifier selon
vos besoins. La page Texte est vide et vous pouvez ajouter des propriétés et
méthodes afin de définir le rôle de l’interface.

Ajout de propriétés et méthodes à une interface ou


dispinterface
Pour ajouter des membres à une interface ou dispinterface
1 Sélectionnez l’interface et choisissez l’icône Propriété ou Méthode dans la
barre d’outils.
Un membre d’interface est ajouté au volet liste des objets et vous pouvez en
saisir le nom.
2 Saisissez le nom du membre.
Le nouveau membre contient les attributs par défaut que vous pouvez modifier
dans la page Attributs.
Vous pouvez également ajouter des méthodes et propriétés directement dans la
page Texte à l’aide de la syntaxe Pascal. Par exemple, vous pouvez saisir les
déclarations de propriétés suivantes dans la page Texte d’une interface :
property AutoSelect: WordBool; dispid 1;
property AutoSize: WordBool; dispid 2;
property BorderStyle: BorderStyle; dispid 3;
Une fois des membres ajoutés à une interface, les membres apparaissent comme
éléments séparés dans le volet liste des objets. Chaque membre dispose de sa
propre page d’attributs vous permettant d’en modifier les caractéristiques.
Si la boîte de dialogue Appliquer les mises à jour est activée, l'éditeur de
bibliothèques de types vous prévient avant de mettre à jour les sources lorsque
vous enregistrez la bibliothèque de types et vous avertit de problèmes éventuels.
Par exemple, si vous renommez un événement par erreur, vous obtenez dans
votre fichier source un avertissement du type suivant :
En raison de la présence de variables d'instance dans votre fichier d'implémentation,
Delphi n'a pas pu mettre à jour le fichier pour refléter la modification du nom de
l'interface d'événement. Bien que Delphi ait mis à jour la
bibliothèque de types, vous devez mettre à jour le fichier d'implémentation manuellement.
Un commentaire TODO précède cet avertissement dans le fichier source.
Remarque Si vous ignorez cet avertissement et le commentaire TODO, le code n’est pas
compilé.

Utilisation des bibliothèques de types 49-33


Création de nouvelles bibliothèques de types

Ajout d’une CoClasse à une bibliothèque de types


Pour ajouter une CoClasse à une bibliothèque de types,
1 Dans la barre d’outils, cliquez sur l’icône CoClass.
Une CoClasse est ajoutée au volet liste des objets et vous pouvez en saisir le
nom.
2 Saisissez le nom de la classe.
La page Attributs de la nouvelle classe contient les attributs par défaut que
vous pouvez personnaliser en fonction du rôle de la classe.
La page Texte est blanche. Pour ajouter des membres à la classe,
3 Cliquez avec le bouton droit de la souris dans la page Texte de la classe afin
d’afficher une liste des interfaces dans laquelle choisir.
La liste inclut les interfaces définies dans la bibliothèque de types en cours et
celles définies par les bibliothèques de types qu’elle référence.
4 Double-cliquez sur l’interface que la classe doit implémenter.
L’interface est ajoutée dans la page avec son GUID et d’autres attributs.

Ajout d’une énumération à une bibliothèque de types


Pour ajouter une énumération à une bibliothèque de types,
1 Dans la barre d’outils, choisissez l’icône enum.
Un type énumération dont vous pouvez saisir le nom est ajouté dans le volet
liste des objets.
2 Saisissez le nom du membre.
La nouvelle énumération est vide et sa page Attributs contient les attributs par
défaut que vous pouvez modifier. Une page Texte vide est également créée.
Ajoutez des valeurs à l’énumération en cliquant sur le bouton Nouvelle
constante. Ensuite, sélectionnez chaque valeur énumérée et assignez ses attributs
à l’aide de la page Attributs.

Enregistrement et recensement des informations


d’une bibliothèque de types
Après avoir modifié votre bibliothèque de types, vous devez enregistrer et
recenser les informations qu’elle contient. Si la bibliothèque de types a été créée
avec l’un des objets ou types de projet de serveur ActiveX, l’enregistrement de la
bibliothèque de types rafraîchit automatiquement la bibliothèque de types
binaire, le code Pascal Objet représentant son contenu et le code
d’implémentation géré à cet effet.

49-34 Guide du développeur


Création de nouvelles bibliothèques de types

L’éditeur de bibliothèques de types stocke les informations de la bibliothèque de


types dans deux formats :
• Sous la forme d’un fichier document composite OLE, appelé projet.TLB.
• Sous la forme d’une unité Delphi.
Cette unité est le résultat de la compilation des déclarations effectuées dans la
bibliothèque de types en termes Pascal Objet. Delphi utilise cette unité pour
associer la bibliothèque de types comme ressource au fichier .OCX ou .EXE.
Modifiez la bibliothèque de types dans l’éditeur de bibliothèques de types, car
l’éditeur génère ces fichiers à chaque sauvegarde de la bibliothèque de types.
Remarque La bibliothèque de types est stockée dans un fichier binaire (.TLB) distinct mais
elle est également liée au serveur (.EXE, DLL ou .OCX).
Remarque Quand on utilise l’éditeur de bibliothèques de types pour des interfaces CORBA,
cette unité définit les objets stub et squelette requis par l’application CORBA.
L’éditeur de bibliothèques de types propose plusieurs options concernant le
stockage des informations de la bibliothèque de types. L’option utilisée dépend
du niveau de l’implémentation de la bibliothèque de types :
• Enregistrer, pour enregistrer sur disque le fichier .TLB et l’unité Delphi.
• Rafraîchir, pour actualiser uniquement en mémoire les unités Delphi de la
bibliothèque de types.
• Recenser, pour ajouter une entrée de la bibliothèque de types aux registres
Windows de votre système.
• Exporter, pour enregistrer un fichier .IDL contenant les définitions de type et
d’interface en format IDL.
Toutes ces méthodes vérifient la syntaxe. Lorsque vous actualisez, recensez ou
enregistrez la bibliothèque de types, Delphi met automatiquement à jour le
fichier source de l'objet associé. Vous pouvez optionnellement réviser les
modifications avant leur validation, si l'option de l'éditeur de bibliothèques de
types Appliquer les mises à jours est activée.

Boîte de dialogue Appliquer les mises à jour


La boîte de dialogue Appliquer les mises à jours apparaît lorsque vous
actualisez, recensez ou enregistrez la bibliothèque de types si vous avez
sélectionné l’option d’affichage des mises à jour avant le rafraichissement dans la
page Outils| Options d'environnement |Bibliothèque de types (option activée
par défaut).
Si cette option n’est pas activée, l'éditeur de bibliothèques de types met
automatiquement à jour les sources de l'objet associé lorsque vous apportez des
modifications dans l'éditeur. Si elle est activée, vous pouvez vous opposer aux
modifications proposées lorsque vous actualisez, enregistrez ou recensez la
bibliothèque de types.

Utilisation des bibliothèques de types 49-35


Création de nouvelles bibliothèques de types

La boîte de dialogue Appliquer les mises à jour vous avertit d'erreurs éventuelles
et insère des commentaires TODO dans le fichier source. Par exemple, si vous
renommez un événement par erreur, vous obtenez dans votre fichier source un
avertissement du type suivant :
En raison de la présence de variables d'instance dans votre fichier d'implémentation,
Delphi n'a pas pu mettre à jour le fichier pour refléter la modification du nom de
l'interface d'événement. Bien que Delphi ait mis à jour la
bibliothèque de types, vous devez mettre à jour le fichier d'implémentation manuellement.
Un commentaire TODO précède cet avertissement dans le fichier source.
Remarque Si vous ignorez cet avertissement et le commentaire TODO, le code n’est pas
compilé.

Enregistrement d’une bibliothèque de types


L’enregistrement d’une bibliothèque de types effectue les actions suivantes :
• Vérification de la syntaxe.
• Enregistrement des informations dans un fichier .TLB.
• Enregistrement des informations dans une unité Delphi.
• Demande au gestionnaire de module de l’EDI d’actualiser l’implémentation si
la bibliothèque de types est associée à un contrôle ActiveX ou à un objet
ActiveForm, ActiveX ou Automation.
Pour enregistrer la bibliothèque de types, choisissez Fichier|Enregistrer dans le
menu principal de Delphi.

Rafraîchissement de la bibliothèque de types


Le rafraîchissement d’une bibliothèque de types effectue les actions suivantes :
• Vérification de la syntaxe.
• Génération, uniquement en mémoire, des unités Delphi de la bibliothèque de
types. L’unité n’est pas enregistrée sur disque.
• Demande au gestionnaire de module d’actualiser l’implémentation si la
bibliothèque de types est associée à un contrôle ActiveX ou à un objet
ActiveForm, ActiveX ou Automation.
Pour rafraîchir la bibliothèque de types, choisissez l’icône Rafraîchir dans la
barre d’outils.
Remarque Si vous avez renommé ou supprimé des éléments de la bibliothèque de types, le
rafraîchissement de l’implémentation peut créer des entrées en double. Dans ce
cas, vous devez déplacer le code pour corriger l’entrée et supprimer les doublons.

Recensement d’une bibliothèque de types


Le recensement d’une bibliothèque de types effectue les actions suivantes :
• Vérification de la syntaxe.
• Ajout aux registres Windows d’une entrée pour la bibliothèque de types.

49-36 Guide du développeur


Déploiement des bibliothèques de types

Pour recenser la bibliothèque de types, choisissez l’icône Recenser de la barre


d’outils.

Exportation d’un fichier IDL


L’exportation d’une bibliothèque de types effectue les actions suivantes :
• Vérification de la syntaxe.
• Création d’un fichier IDL contenant les informations de déclaration de type.
Ce fichier peut décrire les informations de type en IDL CORBA ou en IDL
Microsoft.
Pour exporter une bibliothèque de types, choisissez l’icône Exporter dans la barre
d’outils de l’éditeur de bibliothèques de types.

Déploiement des bibliothèques de types


Par défaut, quand une bibliothèque de types a été créée comme partie d’un
projet de serveur ActiveX, elle est automatiquement liée comme ressource dans
le fichier .DLL, .OCX ou EXE.
Vous pouvez cependant, si vous le préférez, déployer votre application avec la
bibliothèque de types sous la forme d’un fichier .TLB distinct, car Delphi gère la
bibliothèque de types
Historiquement, les bibliothèques de types des applications Automation étaient
stockées dans un fichier distinct d’extension .TLB. Actuellement, dans les
applications Automation standard, les bibliothèques de types sont compilées
directement dans le fichier .OCX ou .EXE. Le système d’exploitation attend que
la bibliothèque de types soit la première ressource dans le fichier exécutable
(.OCX ou .EXE).
Quand vous souhaitez proposer à d’autres développeurs d’applications l’accès à
la bibliothèque de types, elle peut se présenter sous les formes suivantes :
• Une ressource. Cette ressource doit avoir le type TYPELIB et un identificateur
entier. Si vous choisissez de générer une bibliothèque de types avec un
compilateur de ressources, elle doit être déclarée de la manière suivante dans
le fichier ressource (.RC) :
1 typelib mylib1.tlb
2 typelib mylib2.tlb
Il peut y avoir plusieurs ressources bibliothèque de types dans une
bibliothèque ActiveX. Les développeurs d’applications utilisent un compilateur
de ressource pour ajouter le fichier .TLB à leurs propres bibliothèques
ActiveX.
• Un fichier binaire autonome. Le fichier .TLB généré par l’éditeur de
bibliothèques de types est un fichier binaire.

Utilisation des bibliothèques de types 49-37


49-38 Guide du développeur
Chapitre

Création des objets MTS


Chapter 50
50
MTS est un puissant environnement d’exécution qui procure aux applications COM
distribuées les services transactionnels, la sécurité et le pooling des ressources.
Delphi possède un expert Objet MTS quit crée un objet MTS afin que vous
puissiez créer des composants serveur bénéficiant de l’environnement MTS. MTS
apporte de nombreux services permettant de créer des clients et des serveurs
COM, en particulier des serveurs distants, dont l’implémentation est facilitée.
Les composants MTS procurent un certain nombre de services de base, tels que
• Gestion des ressources système, y compris processus, threads et connexions
aux bases de données, afin que votre application serveur puisse gérer de
nombreux utilisateurs simultanés
• Initialisation et contrôle des transactions automatiques afin que votre
application soit fiable
• Création, exécution et suppression des composants serveur, si nécessaire
• Fourniture de la sécurité en fonction des rôles afin que seuls les utilisateurs
autorisés puissent accéder à votre application
En fournissant ces services fondamentaux, MTS vous laisse vous concentrer sur ce
qui est spécifique à votre propre application distribuée. Avec MTS, vous
implémentez la logique de gestion dans des objets MTS, ou dans des modules de
données distants MTS. Avec la construction des composants dans des bibliothèques
(DLL), les DLL sont installées dans l’environnement d’exécution MTS.
Avec Delphi, les serveurs MTS peuvent être des applications indépendantes
comme des ActiveForms. Tout client COM peut s’exécuter au sein de
l’environnement d’exécution MTS.
Ce chapitre donne un aperçu général de la technologie MTS (Microsoft
Transaction Server) et de la façon dont vous pouvez utiliser Delphi pour écrire
des applications basées sur les objets MTS. Delphi fournit également le support
d’un module de données distant MTS, qui est décrit chapitre 14, “Création
d’applications multiniveaux”

Création des objets MTS 50-1


Composants Microsoft Transaction Server

Composants Microsoft Transaction Server


Les composants MTS sont des composants COM pour serveurs en processus,
contenus dans des DLL (Dynamic-Link Libraries). Ils se distinguent des autres
composants COM parce qu’ils s’exécutent dans l’environnement d’exécution
MTS. Vous pouvez créer et implémenter ces composants avec Delphi ou tout
autre outil de développement compatible ActiveX.
Remarque En termes MTS, un composant représente le code qui implémente un objet COM.
Par exemple, les composants MTS sont implémentés dans Delphi en tant que
classes. L’utilisation que fait MTS du terme de composant interfère avec la
traditionnelle utilisation qu’en fait Delphi. Nous utiliserons le mot composant
pour faire référence à une classe ou à un objet descendant de la classe
TComponent. Cependant, pour rester cohérent avec la terminologie MTS, nous
utiliserons les mots composant MTS lorsque nous parlerons spécifiquement des
classes MTS. A la fois dans MTS et dans Delphi, nous utiliserons le mot objet
pour faire référence à l’instance d’un composant MTS.
Habituellement, les objets serveur MTS sont de petite taille et servent à des
fonctions de gestion particulières. Par exemple, des composants MTS peuvent
implémenter les règles de gestion d’une application, en fournissant les vues et
les transformations de l’état de l’application. Considérons l’exemple d’une
application médicale destinée à un médecin. Les enregistrements médicaux
stockés dans plusieurs bases de données donnent l’état permanent de
l’application, par exemple l’historique de la santé des patients. Les composants
MTS mettent à jour cet état afin qu’il reflète les modifications qui lui sont
apportées, par exemple les nouveaux patients, les résultats d’analyses sanguines
ou les fichiers des radiographies.
Comme le montre la figure suivante, un objet MTS peut être vu comme
n’importe quel autre objet COM. Non seulement il supporte un nombre
quelconque d’interfaces COM, mais il supporte également les interfaces MTS.
Exactement comme IUnknown est l’interface commune à tous les objets COM,
IObjectControl est commune à tous les objets MTS. IObjectControl contient les
méthodes permettant d’activer et de désactiver l’objet MTS ainsi que gérer des
ressources telles que les connexions aux bases de données.
Figure 50.1 Interface d’un objet MTS

Le client d’un serveur au sein de l’environnement MTS est appelé un client de


base. Vu par le client de base, un objet COM dans l’environnement MTS
ressemble à tout autre objet COM. L’objet MTS est installé en tant que .DLL au

50-2 Guide du développeur


Composants Microsoft Transaction Server

sein de l’exécutable MTS. En s’exécutant avec l’EXE MTS, les objets MTS
bénéficient des fonctionnalités de l’environnement d’exécution MTS comme le
pooling des ressources ou le support des transactions.
L’exécutable MTS (.EXE) peut être exécuté dans le même processus que le client
de base comme le montre la figure 50.2.
Figure 50.2 Composant MTS en processus

Le composant MTS peut être installé sur un processus serveur distant au sein de
la même machine comme le montre la figure suivante. Le client de base parle à
un proxy qui effectue le marshaling de la requête du client pour le stub du
composant MTS, qui à son tour accède au composant MTS via son interface.
Figure 50.3 Un composant MTS dans un serveur hors processus

Le composant MTS peut être installé dans un processus serveur distant sur un
ordinateur séparé comme le montre la figure suivante. Exactement comme dans
tout autre processus serveur distant, le client et le serveur distant communiquent
au travers de machines utilisant DCOM.
Figure 50.4 Un composant MTS dans un processus serveur distant

Création des objets MTS 50-3


Composants Microsoft Transaction Server

Les informations de connexion sont maintenues sur le proxy MTS. La connexion


entre le client MTS et le proxy reste ouverte aussi longtemps que le client
demande la connexion au serveur, il semble de ce fait au client qu’il bénéficie
d’un accès continu au serveur. En réalité, le stub MTS peut désactiver et
réactiver l’objet, conservant des ressources pour que les autres clients puissent
utiliser la connexion. Pour plus d’informations sur l’activation et la désactivation,
voir “Gestion des ressources avec l’activation just-in-time et le pooling” à la
page 50-5.

Exigences d’un composant MTS


Hormis les exigences relatives à COM, MTS nécessite que le composant soit une
DLL (Dynamic-Link Library). Les composants implémentés en tant
qu’exécutables (fichiers .EXE) ne peuvent pas s’exécuter dans l’environnement
d’exécution MTS.
De plus, un composant MTS doit répondre aux exigences suivantes :
• Si vous utilisez l’expert objet MTS, le composant doit avoir un fabricant de classe
standard fourni automatiquement par Delphi.
• Le composant doit présenter son objet classe en exportant la méthode
DllGetClassObject standard.
• Toutes les interfaces et les coclasses du composant doivent être décrites par
une bibliothèque de types, fournie par l’expert objet MTS. Vous pouvez
ajouter des méthodes et propriétés à la bibliothèque de types en utilisant
l’éditeur de bibliothèques de types. La bibliothèque de types est utilisée par
l’explorateur MTS pour en extraire les informations concernant les composants
installés au cours de l’exécution.
• Le composant peut exporter uniquement les interfaces utilisant le marshaling
COM standard, automatiquement fourni par l’expert objet MTS.
• La version Delphi de MTS ne permet pas le marshaling manuel pour les
interfaces personnalisées. Toutes les interfaces doivent être implémentées en
tant qu’interfaces doubles utilisant le support du marshaling automatique de
COM.
• Le composant doit exporter la fonction DllRegisterServer et effectuer l’auto-
enregistrement de ses CLSID, ProgID, interfaces et bibliothèque de types dans
cette routine. Tout cela est fourni par l’expert objet MTS.
• Un composant exécuté dans l’espace de processus MTS ne peut pas s’agréger aux
composants non exécutés en MTS..

50-4 Guide du développeur


Gestion des ressources avec l’activation just-in-time et le pooling

Gestion des ressources avec l’activation just-in-time


et le pooling
MTS gère les ressources en fournissant
• Activation just-in-time
• Pooling des ressources
• Pooling des objets

Activation just-in-time
La capacité d’un objet à être désactivé et réactivé alors que des clients lui font
référence est nommée activation just-in-time. Vu du client, il existe une seule
instance de l’objet à partir du moment où le client l’a créée et jusqu’au moment
où il la libère. En réalité, il se peut que l’objet ait été désactivé et réactivé de
nombreuses fois. Les objets pouvant être ainsi désactivés, les clients peuvent
continuer de faire référence à un objet pendant un temps indéterminé sans
affecter les ressources du système. Lorsqu’un objet devient désactivé, MTS libère
toutes les ressources de l’objet, par exemple, sa connexion à la base de données.
Lorsqu’un objet COM est créé en tant qu’élément de l’environnement MTS, un
objet de contexte est également créé. Cet objet de contexte existe pendant
l’intégralité de la durée de vie de l’objet MTS correspondant, pendant un ou
plusieurs cycles de réactivation. MTS utilise l’objet de contexte pour conserver la
trace de l’objet durant la désactivation. Cet objet de contexte, accessible via
l’interface IObjectContext, coordonne les transactions. Un objet COM est créé dans
l’état désactivé et devient actif lorsqu’il reçoit la demande d’un client.
Un objet MTS est désactivé lorsqu’un des événement suivants se produit :
• L’objet demande la désactivation avec SetComplete ou SetAbort : Un objet
appelle la méthode IObjectContext SetComplete lorsqu’il a terminé son travail
avec succès et qu’il n’a pas besoin de sauver l’état interne de l’objet pour un
prochain appel du client. Un objet appelle SetAbort pour indiquer qu’il ne
peut terminer son travail avec succès et que l’état de l’objet n’a pas besoin
d’être sauvé. C’est-à-dire que l’objet reprend l’état qu’il avait avant la
transaction. Souvent, les objets sont conçus sans état, ce qui signifie que les
objets sont désactivés après le retour de toutes les méthodes.
• Une transaction est validée ou annulée : Lorsqu’une transaction sur un objet
est validée ou annulée, l’objet est désactivé. Parmi ces objets désactivés, les
seuls qui continuent à exister sont ceux à qui des clients hors transaction sont
en train de faire référence. Les prochains appels à ces objets les réactivent et
les font s’exécuter dans la prochaine transaction.
• Le dernier client libère l’objet : Bien sûr, lorsqu’un client libère l’objet, il est
désactivé et le contexte d’objet également.

Création des objets MTS 50-5


Gestion des ressources avec l’activation just-in-time et le pooling

Pooling des ressources


Puisque MTS libère les ressources système inactives lors de la désactivation, les
ressources libérées sont disponibles pour d’autres objets serveur. Donc, la
connexion à une base de données qui n’est plus utilisée par un objet serveur
peut être réutilisée par un autre client. C’est ce que l’on apelle le pooling des
ressources.
L’ouverture et la fermeture des connexions à une base de données sont des
opérations qui prennent du temps. MTS utilise des dispenseurs de ressources
pour offrir le moyen de réutiliser les connexions existantes au lieu d’en créer de
nouvelles. Un dispenseur de ressources met en cache des ressources comme les
connexions à une base de données, de sorte que les composants du même
paquet puissent les partager. Par exemple, si vous avez un composant de
recherche ou de mise à jour dans une base de données s’exécutant dans une
application de maintenance personnalisée, vous devez empaqueter ensemble ces
composants pour qu’ils puissent partager les connexions à la base de données.
Dans l’environnement Delphi, le dispenseur de ressources est le BDE (Borland
Database Engine).
Lors du développement des applications MTS, vous devez vous charger de la
libération des ressources

Libération des ressources


Vous êtes responsable de la libération des ressources d’un objet. Habituellement,
vous le faites en appelant les méthodes SetComplete et SetAbort après avoir servi
la demande de chaque client. Ces méthodes libèrent les ressources allouées par le
dispenseur de ressources MTS.
Au même moment, vous devez libérer les références à toutes les autres
ressources, y compris les références aux autres objets (notamment les objets MTS
et les objets de contexte) ainsi que la mémoire occupée par toutes les instances
du composant, comme vous utilisez free en Pascal Objet.
La seule fois où vous omettrez ces appels, c’est lorsque vous voudrez maintenir
l’état entre les appels client. Pour plus d’informations, voir “Objets avec état et
sans état” à la page 50-9.

Pooling des objets


Tout comme MTS a été conçu pour mettre en pool des ressources, il peut
également mettre en pool des objets. Après que MTS a appelé la méthode
désactivée, il appelle la méthode CanBePooled, qui indique si l’objet peut être mis
en pool en vue de sa réutilisation. Si CanBePooled est définie par TRUE, au lieu
de détruire l’objet après la désactivation, MTS place l’objet dans le pool d’objets.
Les objets présents dans le pool d’objets sont disponibles pour une utilisation
immédiate par n’importe quel autre client demandant cet objet. C’est uniquement
lorsque le pool d’objets est vide que MTS crée une nouvelle instance de l’objet.

50-6 Guide du développeur


Support transactionnel MTS

Les objets qui renvoient FALSE ou qui ne supportent pas l’interface


IObjectControl sont détruits.
Remarque Le pooling des objets et leur recyclage ne sont pas disponibles dans cette version
de MTS. MTS appelle CanBePooled comme nous l’avons dit, mais aucun pooling
n’est effectué. C’est par souci de compatibilité que nous permettons aux
développeurs d’utiliser CanBePooled dans leurs applications, afin qu’elles puissent
gérer le pooling dès qu’il sera disponible. Actuellement, et tant que le pooling
des objets n’est pas disponible dans MTS, Delphi initialise CanBePooled à FALSE.

Accès au contexte d’un objet


Comme tout objet COM, un objet COM utilisant MTS doit être créé avant d’être
utilisé. Les clients COM créent un objet en appelant la fonction de la
bibliothèque COM, CoCreateInstance.
Chaque objet COM s’exécutant dans l’environnement MTS, doit correspondre à
un objet de contexte. Cet objet de contexte est implémenté automatiquement par
MTS. Il est utilisé pour gérer le composant MTS et coordonner les transactions.
L’interface de l’objet de contexte est IObjectContext. Pour accéder à la majorité
des méthodes du contexte de l’objet, vous pouvez utiliser la propriété
ObjectContext de l’objet TMtsAutoObject. Par exemple, vous pouvez utiliser la
propriété ObjectContext comme suit :
if ObjectContext.IsCallerInRole (‘Manager’) ...
Il existe une autre façon d’accéder au contexte de l’objet : utiliser les méthodes
de l’objet TMtsAutoObject :
if IsCallerInRole (‘Manager’) ...
Vous pouvez utiliser l’une ou l’autre des méthodes précédentes. Cependant, il
existe un léger avantage à utiliser les méthodes TMtsAutoObject plutôt qu’à faire
référence à la propriété ObjectContext lorsque vous testez votre application. Pour
la présentation de ces différences, voir “Débogage et test des objets MTS” à la
page 50-22

Support transactionnel MTS


Le support des transactions procuré par MTS vous permet de grouper des
actions au sein des transactions. Par exemple, dans une application
d’enregistrements médicaux, si vous avez un composant Transfert pour transférer
des enregistrements d’un médecin à l’autre, vous pouvez avoir vos méthodes
Add et Delete dans la même transaction. De cette façon, soit l’intégralité de
Transfert fonctionne, soit il peut être ramené à son état précédent. Les
transactions simplifient les corrections d’erreurs dans les applications devant
accéder à plusieurs bases de données.

Création des objets MTS 50-7


Support transactionnel MTS

Les transactions MTS garantissent ce qui suit :


• Toutes les mises à jour d’une même transaction sont soit validées, soit
annulées et ramenées à leur état précédent. C’est ce que nous appelons
atomicité.
• Une transaction est une transformation valide de l’état d’un système, en
maintenant les constantes de cet état. C’est ce que nous appelons cohérence.
• Les transactions simultanées ne voient pas les résultats partiels et non validés
les unes des autres afin de ne pas créer d’incohérences dans l’état de
l’application. C’est ce que nous appelons isolation. Les gestionnaires de
ressources utilisent des protocoles de synchronisation basés sur les
transactions pour isoler les travaux non validés des transactions actives.
• Les mises à jour validées des ressources gérées (comme les enregistrements
d’une base de données) survivent aux pannes, y compris les pannes de
communication, les pannes de processus et les pannes système des serveurs.
C’est ce que nous appelons durabilité. L’ouverture d’une transaction vous
permet de bénéficier d’un état durable après les pannes survenues sur les
supports disque.
Lorsque vous déclarez qu’un composant MTS fait partie d’une transaction, MTS
associe des transactions avec les objets du composant. Lorsque la méthode d’un
objet est exécutée, les services que les gestionnaires et les dispenseurs de
ressources réalisent pour lui s’exécutent sous le contrôle d’une transaction. Le
travail de multiples objets peut être rassemblé en une unique transaction.

Attributs transactionnels
Tout composant MTS a un attribut transactionnel enregistré dans le catalogue
MTS. Le catalogue MTS maintient les informations de configuration des
composants, paquets et rôles. Vous administrez le catalogue en utilisant
l’explorateur MTS, comme décrit dans “Administration des objets MTS avec
l’explorateur MTS” à la page 50-24.
Chaque attribut transactionnel peut être défini par une des valeurs suivantes :

Requiert une Les objets MTS doivent s’exécuter dans la portée d’une
transaction transaction. Lorsqu’un nouvel objet est créé, son contexte
hérite de la transaction du contexte du client. Si le client n’a
pas de contexte transactionnel, MTS crée automatiquement
un nouveau contexte transactionnel pour l’objet.
Requiert une Les objets MTS doivent s’exécuter dans leurs propres
nouvelle transaction transactions. Lorsqu’un nouvel objet est créé, MTS crée
automatiquement une nouvelle transaction pour cet objet,
que son client ait ou non une transaction. Un objet ne
s’exécute jamais dans la portée de la transaction de son
client. Au contraire, le système crée toujours des
transactions indépendantes pour les nouveaux objets.

50-8 Guide du développeur


Support transactionnel MTS

Supporte les Les objets MTS peuvent s’exécuter dans la portée des
transactions transactions de leur client. Lorsqu’un nouvel objet est créé,
son contexte hérite de la transaction du contexte du client.
Cela permet à plusieurs objets d’être rassemblés dans une
même transaction. Si le client n’a pas de transaction, le
nouveau contexte est également créé sans transaction.
Ne supporte pas les Les objets MTS ne s’exécutent pas dans la portée des
transactions transactions. Lorsqu’un nouvel objet est créé, son contexte
d’objet est créé sans transaction, que son client ait ou non
une transaction. Utilisez cela pour les objets COM conçus
avant le support de MTS.

Le contexte d’objet contient l’attribut de la transaction


Le contexte associé à un objet indique si l’objet est exécuté dans une transaction
et, si oui, quelle est l’identification de cette transaction.
Les dispenseurs de ressources peuvent utiliser l’objet de contexte pour fournir
des services transactionnels à l’objet MTS. Par exemple, lorsqu’un objet
s’exécutant dans une transaction alloue une connexion à une base de données en
utilisant le dispenseur de ressources BDE, la connexion est automatiquement
intégrée à la transaction. Toutes les mises à jour de la base utilisant cette
connexion deviennent partie intégrante de la transaction et sont soit validées soit
annulées. Pour avoir plus d’informations, voir "Enlisting Resources in
Transactions" dans la documentation MTS.

Objets avec état et sans état


Comme tous les objets COM, les objets MTS sont capables de maintenir un état
interne au cours de leurs multiples interactions avec un client. Un tel objet est
dit avec état. Les objets MTS peuvent également être sans état, ce qui signifie
que l’objet ne maintient aucun état intermédiaire pendant l’attente du prochain
appel d’un client.
Lorsqu’une transaction est validée ou annulée, tous les objets qui étaient inclus
dans la transaction sont désactivés, ce qui fait qu’ils perdent l’état qu’ils avaient
acquis au cours de la transaction. C’est un des mécanismes qui garantissent
l’isolation des transactions et la cohérence des données ; cela libère également les
ressources du serveur pour leur utilisation par d’autres transactions. La fin d’une
transaction permet à MTS de désactiver un objet et de récupérer ses ressources.
Pour davantage d’informations sur la manière dont MTS libère l’objet état, voir
la section suivante.
Le maintien de l’état d’un objet nécessite que l’objet reste activé, en conservant
des ressources potentiellement valables, comme les connexions à des bases de
données.
Remarque Les objets sans état sont plus efficaces et sont, de ce fait, recommandés.

Création des objets MTS 50-9


Support transactionnel MTS

Activation de plusieurs objets pour supporter les


transactions
Utilisez les méthodes IObjectContext, comme le montre le tableau suivant, pour
qu’un objet MTS puisse déterminer la façon dont se termine une transaction. Ces
méthodes, avec l’attribut transactionnel du composant, vous permettent
d’engager un ou plusieurs objets dans une transaction.

Tableau 50.1 Méthodes IObjectContext pour le support des transactions


Méthode Description
SetComplete Indique que l’objet a terminé son travail avec succès pour la transaction.
L’objet est désactivé après le retour de la méthode entrée la première
dans le contexte. MTS réactive l’objet lors du prochain appel demandant
l’exécution de celui-ci.
SetAbort Indique que le travail de l’objet ne pourra jamais être validé. L’objet est
désactivé au retour de la méthode entrée la première dans le contexte. MTS
réactive l’objet lors du prochain appel demandant l’exécution de celui-ci.
EnableCommit Indique que le travail de l’objet n’est pas obligatoirement terminé mais
que les mises à jour transactionnelles peuvent être validées dans leur
forme actuelle. Utilisez cela pour conserver l’état entre les multiples
appels d’un client. Lorsqu’un objet appelle EnableCommit, il autorise la
validation de la transaction à laquelle il participe mais il maintient son
état interne entre les appels de ses clients jusqu’à ce qu’il appelle
SetComplete ou SetAbort ou que la transaction se termine.
EnableCommit est l’état par défaut lorsqu’un objet est activé. C’est
pourquoi un objet doit toujours appeler SetComplete ou SetAbort avant de
retourner d’une méthode, sauf si vous voulez que l’objet maintienne son
état interne jusqu’au prochain appel d’un client.
DisableCommit Indique que le travail de l’objet est incohérent et qu’il ne peut se
terminer tant que l’objet n’a pas reçu de nouvelles invocations de
méthodes de la part du client. Appelez cette méthode avant de rendre le
contrôle au client afin de maintenir l’état entre plusieurs appels client.
Cela empêche l’environnement d’exécution MTS de désactiver l’objet et
de réclamer ses ressources au retour de l’appel d’une méthode.
Lorsqu’un objet a appelé DisableCommit, si un client tente de valider la
transaction avant que l’objet ait appelé EnableCommit ou SetComplete,
la transaction est annulée.
Par exemple, vous pouvez utiliser cela pour modifier l’état par défaut
lorsqu’un objet est activé.

Transactions contrôlées par MTS ou par le client


Les transactions peuvent être contrôlées directement par le client ou
automatiquement par l’environnement d’exécution MTS.
Les clients peuvent avoir un contrôle direct sur les transactions en utilisant un
objet de contexte transactionnel (via l’interface ITransactionContext). Cependant,
MTS est conçu pour simplifier le développement client en prenant
automatiquement en charge la gestion des transactions.

50-10 Guide du développeur


Support transactionnel MTS

Les composants MTS peuvent être déclarés de sorte que leurs objets s’exécutent
toujours dans une transaction, quelle que soit la façon dont ces objets ont été
créés. Ainsi, les objets n’ont pas besoin d’inclure de logique spéciale pour gérer
le cas où un objet est créé par un client n’utilisant pas les transactions. Cette
caractéristique réduit également le volume des applications client. Les clients
n’ont aucun besoin d’initialiser une transaction simplement parce que le
composant qu’ils utilisent l’exige.
Avec les transactions MTS, vous pouvez implémenter la logique de gestion de
votre application dans les objets serveur. Les objets serveur peuvent imposer les
règles de sorte que le client n’ait pas besoin de s’en préoccuper. Par exemple,
dans une application médicale, un client technicien radiologue peut ajouter et
voir les radiographies d’un enregistrement médical. Il n’a pas besoin de savoir
que l’application ne permet pas aux techniciens radio d’ajouter ou de voir tout
autre type d’enregistrement. Cette logique se trouve dans les autres objets
serveur de l’application.

Avantage des transactions


Permettre à un composant de vivre dans sa propre transaction ou de faire partie
d’un plus vaste groupe de composants appartenant à une même transaction est le
principal avantage de l’environnement d’exécution MTS. Cela permet à un
composant d’être utilisé de différentes façons. Cela permet donc aux développeurs
de réutiliser leur code dans différentes applications sans réécrire la logique de
l’application. En fait, les développeurs peuvent choisir comment les composants
seront utilisés dans les transactions au moment où ils les empaquettent. Ils peuvent
modifier le comportement transactionnel simplement en ajoutant un composant à
un autre paquet. Pour plus d’informations sur les paquets de composants, voir
“Installation des objets MTS dans un paquet MTS” à la page 50-23

Temporisation des transactions


La temporisation des transactions définit pendant combien de temps (exprimé en
secondes) une transaction peut rester active. Les transactions toujours vivantes
après le délai de temporisation sont automatiquement annulées par le système.
Par défaut, cette valeur est de 60 secondes. Vous pouvez désactiver la
temporisation des transactions en spécifiant la valeur 0, cela peut être utile lors
du débogage des objets MTS.
Pour définir la temporisation sur votre ordinateur,
1 Dans l’explorateur MTS, sélectionnez Poste de travail.
Par défaut, Poste de travail correspond à l’ordinateur local sur lequel MTS est
installé.
2 Cliquez avec le bouton droit et choisissez Propriétés. Choisissez ensuite
l’onglet Options.
L’onglet Options est utilisé pour définir la propriété de temporisation des
transactions de votre ordinateur.

Création des objets MTS 50-11


Sécurité en fonction des rôles

3 Spécifiez 0 comme valeur de temporisation pour désactiver la temporisation


des transactions.
4 Cliquez sur OK pour enregistrer cette définition et revenir à l’explorateur MTS.
Pour plus d’informations sur le débogage des applications MTS, voir “Débogage et
test des objets MTS” à la page 50-22.

Sécurité en fonction des rôles


Actuellement, MTS offre une sécurité en fonction des rôles dans laquelle vous
attribuez un rôle à chaque groupe d’utilisateurs. Par exemple, notre application
médicale pourrait définir un rôle pour les médecins, un pour les techniciens
radiologue et un pour les patients.
Vous définissez des autorisations pour chaque composant et chaque interface de
composant en assignant des rôles. Par exemple, dans une application médicale,
seuls les médecins seraient autorisés à voir tous les enregistrements médicaux,
les techniciens radio ne verraient que les radiographies, et les patients leurs
propres données.
Habituellement, les rôles sont définis au cours du développement de
l’application et ils sont attribués à chacun des paquets de composants. Ils sont
ensuite assignés à des utilisateurs spécifiques lorsque l’application est déployée.
Les administrateurs peuvent configurer les rôles en utilisant l’explorateur MTS.
Vous pouvez aussi définir les rôles par programmation en utilisant la propriété
ObjectContext TMtsAutoObject. Par exemple,
if ObjectContext.IsCallerInRole (‘Manager’) ...
Une autre façon d’avoir accès au contexte de l’objet est d’utiliser les méthodes de
l’objet MTS, TMtsAutoObject :
if IsCallerInRole (‘Manager’) ...
Remarque Pour les applications nécessitant une sécurité renforcée, les objets contexte
implémentent l’interface ISecurityProperty dont les méthodes permettent d’obtenir
l’identificateur de sécurité Window (SID) de l’appelant direct et créateur de
l’objet ainsi que le SID des clients utilisant l’objet.

Dispenseurs de ressources
Un dispenseur de ressources gère l’état partagé non durable de la part des
composants de l’application dans un processus. Les dispenseurs de ressources
sont semblables aux gestionnaires de ressources comme SQL Server, mais sans
garantie de durabilité. Delphi fournit deux dispenseurs de ressources :
• Dispenseur de ressources BDE
• Gestionnaire de propriétés partagées

50-12 Guide du développeur


Dispenseurs de ressources

Dispenseur de ressources BDE


Le dispenseur de ressources BDE gère les pools de connexions aux bases de
données pour les composants MTS qui utilisent les interfaces de bases de
données standard, allouant les connexions aux objets avec rapidité et efficacité.
Pour les modules de données MTS distants, les connexions sont
automatiquement intégrées aux transactions des objets, et le dispenseur de
ressources peut automatiquement récupérer et réutiliser les connexions.

Gestionnaire de propriétés partagées


Le gestionnaire de propriétés partagées est un dispenseur de ressources que vous
pouvez utiliser pour partager un état entre plusieurs objets au sein d’un
processus serveur. Par exemple, vous pouvez l’utiliser pour maintenir un état
partagé entre les divers participants à un jeu multi-utilisateurs.
Le gestionnaire de propriétés partagées vous évite l’ajout à votre application
d’une grande quantité de code, car MTS fournit le support à votre place. C’est-à-
dire que le gestionnaire de propriétés partagées conserve l’état des objets en
implémentant des verrous et des sémaphores afin de protéger des accès
simultanés les propriétés partagées. Le gestionnaire de propriétés partagées évite
les conflits de noms en utilisant des groupes de propriétés partagées qui
établissent des domaines d’appellation uniques pour les propriétés partagées
qu’ils contiennent.
Pour utiliser la ressource gestionnaire de propriétés partagées, utilisez d’abord la
fonction d’aide CreateSharedPropertyGroup pour créer un groupe de propriétés
partagées. Ensuite, vous pouvez écrire et lire toutes les propriétés de ce groupe.
En utilisant un groupe de propriétés partagées, les informations de l’état sont
sauvegardées au travers de toutes les désactivations de l’objet MTS. En outre, les
informations d’état peuvent être partagées entre tous les objets MTS installés
dans le même paquet. Vous pouvez installer les composants MTS dans un
paquet comme décrit dans “Installation des objets MTS dans un paquet MTS” à
la page 50-23.
L’exemple suivant montre comment ajouter le code supportant la ressource
gestionnaire de propriétés partagées à un objet MTS. Après cet exemple, vous
trouverez des conseils à prendre en compte lorsque vous concevrez votre
application MTS et que vous voudrez partager des propriétés.

Exemple : Partage de propriétés entre les instances d’un objet


MTS
L’exemple suivant crée un groupe de propriétés appelé MyGroup afin qu’il
contienne les propriétés à partager entre les objets et les instances des objets. Cet
exemple illustre le partage d’une propriété appelée Counter. Il utilise la fonction
d’aide CreateSharedPropertyGroup pour créer le gestionnaire du groupe de
propriétés et le groupe de propriétés, puis la méthode CreateProperty de l’objet
Group pour créer la propriété Counter.

Création des objets MTS 50-13


Dispenseurs de ressources

Pour obtenir la valeur d’une propriété, utilisez la méthode PropertyByName de


l’objet Group, comme ci-dessous. Vous pouvez également utiliser la méthode
PropertyByPosition.
unit Unit1;
interface
uses
MtsObj, Mtx, ComObj, Project2_TLB;
type
Tfoobar = class(TMtsAutoObject, Ifoobar)
private
Group: ISharedPropertyGroup;
protected
procedure OnActivate; override ;
procedure OnDeactivate; override ;
procedure IncCounter;
end;
implementation
uses ComServ;
{ Tfoobar }
procedure Tfoobar.OnActivate;
var
Exists: WordBool;
Counter: ISharedProperty;
begin
Group := CreateSharedPropertyGroup('MyGroup');
Counter := Group.CreateProperty('Counter', Exists);
end;
procedure Tfoobar.IncCounter;
var
Counter: ISharedProperty;
begin
Counter := Group.PropertyByName['Counter'];
Counter.Value := Counter.Value + 1;
end;
procedure Tfoobar.OnDeactivate;
begin
Group := nil;
end;
initialization
TAutoObjectFactory.Create(ComServer, Tfoobar, Class_foobar, ciMultiInstance,
tmApartment);
end.

Conseils d’utilisation du gestionnaire de propriétés partagées


Pour que les objets puissent partager un état, ils doivent s’exécuter dans le
même processus.
Vous ne pouvez partager des propriétés qu’entre des objets s’exécutant dans le
même processus. Si vous voulez que des instances de différents composants
partagent des propriétés, vous devez installer les composants dans le même
paquet MTS. Comme les administrateurs risquent de déplacer les composants
d’un paquet à un autre, le plus sûr est de limiter l’utilisation d’un groupe de
propriétés partagées aux instances des composants définies dans la même DLL.

50-14 Guide du développeur


Clients de base et composants MTS

Les composants partageant des propriétés doivent avoir le même attribut


d’activation. Si deux composants du même paquet ont des attributs d’activation
différents, ils ne pourront généralement pas partager de propriétés. Par exemple,
si un composant est configuré pour s’exécuter dans un processus client et l’autre
dans un processus serveur, leurs objets s’exécuteront habituellement dans des
processus différents, même s’ils appartiennent au même paquet.

Clients de base et composants MTS


Il est important de bien comprendre la différence entre clients et objets dans
l’environnement d’exécution MTS. Les clients, ou clients de base en termes MTS,
ne s’exécutent pas sous MTS. Les clients de base sont les principaux
consommateurs d’objets MTS. Habituellement, ils fournissent l’interface
utilisateur de l’application ou font se correspondre les requêtes des utilisateurs
finaux aux fonctions de gestion définies dans les objets serveur MTS. D’autre
part, les clients ne profitent pas des caractéristiques fondamentales de MTS. Les
clients ne bénéficient pas du support des transactions ni des dispenseurs de
ressources.
Le tableau suivant compare les composants MTS et les applications client de
base.

Tableau 50.2 Objets serveur MTS et clients de base


Composants MTS Clients de base
Les composants MTS sont contenus dans Les clients de base sont écrits en tant que
des DLL (Dynamic-Link Libraries) COM ; fichiers exécutables (EXE) ou en tant que DLL
MTS charge les DLL dans les processus, à la (Dynamic-Link Libraries). MTS n’est pas
demande. impliqué dans leur déclenchement ni dans leur
chargement.
MTS gère les processus serveur qui MTS ne gère pas les processus client de base.
hébergent les composants MTS.
MTS crée et gère les threads utilisés par les MTS ne crée pas et ne gère pas les threads
composants. utilisés par les applications client de base.
Tout objet MTS est associé à un objet de Les clients de base ne possèdent pas d’objet de
contexte. MTS crée, gère et libère contexte implicite. Ils peuvent utiliser objets de
automatiquement les objets de contexte. contexte transactionnels, mais ils doivent les
créer, les gérer et les libérer explicitement.
Les objets MTS peuvent utiliser les Les clients de base ne peuvent pas utiliser les
dispenseurs de ressources. Les dispenseurs dispenseurs de ressources.
de ressources ont accès à l’objet de contexte,
ce qui permet aux ressources acquises d’être
automatiquement associées à ce contexte.

Création des objets MTS 50-15


Technologies sous-jacentes de MTS, COM et DCOM

Technologies sous-jacentes de MTS, COM et DCOM


MTS utilise COM (Component Object Model) comme base pour supporter les
objets COM dans les applications client/serveur. COM définit un ensemble
d’interfaces structurées qui permettent aux composants de communiquer.
Pour les communications distantes, MTS utilise DCOM. Pour accéder à un objet
COM situé sur une autre machine, le client utilise DCOM qui transfère de
manière transparente la demande locale à l’objet distant s’exécutant sur une
autre machine. Pour les appels de procédures distantes, DCOM utilise le
protocole RPC fourni par DEC (Distributed Computing Environment) d’Open
Group.
Pour la sécurité distribuée, DCOM utilise le protocole de sécurité NTLM (NT
Lan Manager). Pour les services de répertoires, DCOM utilise le DNS (Domain
Name System).
Le pooling des ressources dans l’environnement MTS est généralement apporté
par le moteur de base de données sous-jacent. Dans Delphi, le pooling des
ressources est fourni par le BDE (Borland Database Engine). Toutes les
connexions aux bases de données allouées par les objets MTS proviennent de ce
groupe. Ces connexions peuvent participer aux commit en deux phases sous le
contrôle de MTS.

Présentation de la création des objets MTS


Le procédé de création d’un composant MTS est le suivant :
1 Utilisez l’expert objet MTS pour créer un composant MTS.
2 Ajoutez des méthodes et des propriétés à l’application en utilisant l’éditeur de
bibliothèques de types. .Pour plus de détails concernant l’ajout des méthodes et
des propriétés en utilisant l’éditeur de bibliothèques de types, voir chapitre 49,
“Utilisation des bibliothèques de types”.
3 Déboguez et testez le composant MTS.
4 Installez le composant MTS dans un paquet MTS nouveau ou pré-existant.
5 Administrez l’environnement MTS en utilisant l’explorateur MTS.

Utilisation de l’expert objet MTS


Utilisez l’expert objet MTS pour créer un objet MTS permettant aux applications
client d’accéder à votre serveur au sein de l’environnement d’exécution MTS.
MTS fournit un support à l’exécution extrêmement puissant qui inclut le pooling
des ressources, le traitement transactionnel et la sécurité en fonction des rôles.

50-16 Guide du développeur


Utilisation de l’expert objet MTS

Pour faire apparaître l’expert objet MTS,


1 Choisissez Fichier|Nouveau.
2 Sélectionnez l’onglet libellé Multi-niveaux.
3 Cliquez deux fois sur l’icône objet MTS.
Dans l’expert, spécifiez ce qui suit :

Nom de classe Spécifiez le nom de la classe MTS.


Modèle de thread Choisissez un modèle de thread afin d’indiquer
comment les applications client peuvent appeler
l’interface de votre objet. C’est le modèle de thread que
vous validez pour l’implémentation dans l’objet MTS.
Pour plus d’informations sur les modèles de threads,
voir “Choix d’un modèle de thread” à la page 45-4.
Remarque : Le modèle de thread choisi détermine
comment l’objet sera recensé. Vous devez être certain
que l’implémentation de votre objet est conforme au
modèle sélectionné.
Modèle de transaction Spécifiez si et comment l’objet MTS supporte les
transactions
Générer le code de Cochez cette case pour indiquer à l’expert d’implémenter
support d'événement une interface séparée pour gérer les événements de
l’objet MTS.

Lorsque vous suivez cette procédure, une nouvelle unité est ajoutée au projet en
cours contenant la définition de l’objet MTS. De plus, l’expert ajoute un projet
bibliothèque de types et ouvre la bibliothèque. Alors, vous pouvez exposer les
propriétés et les méthodes de l’interface par le biais de la bibliothèque de types.
Vous exposez l’interface comme vous exposeriez n’importe quel objet
Automation comme décrit dans “Exposition des propriétés, méthodes et
événements d’une application” à la page 47-3.
L’objet MTS implémente une interface double, qui supporte à la fois l’édition de
liens précoce (lors de la compilation), via la vtable, et l’édition de liens tardive
(lors de l’exécution), via l’interface IDispatch.
L’expert objet MTS implémente les méthodes de l’interface IObjectControl,
Activate, Deactivate et CanBePooled.

Choix d’un modèle de thread pour un objet MTS


L’environnement d’exécution MTS gère les threads à votre place. Les composants
MTS ne doivent pas créer de threads. Les composants ne doivent jamais terminer
un thread qui fait un appel dans une DLL.

Création des objets MTS 50-17


Utilisation de l’expert objet MTS

Lorsque vous spécifiez le modèle de thread en utilisant l’expert MTS, vous


spécifiez comment les objets seront attribués aux threads lors de l’exécution des
méthodes.

Tableau 50.3 Modèles de threads pour les objets COM


Modèle de thread Description Avantages et inconvénients
Unique Pas de support des threads. Les Permet aux composants d’utiliser des
demandes client sont sérialisées par le bibliothèques non ré-entrantes.
mécanisme d’appel. Capacités d’évolution extrêmement
Tous les objets d’un composant à limitées.
thread unique s’exécutent dans le Les composants à thread unique et avec
thread principal. état sont prédisposés aux étreintes
Cela est compatible avec le modèle de mortelles. Vous pouvez éliminer ce
thread COM par défaut utilisé pour les problème en utilisant des objets sans
composants ne possédant pas état et en appelant SetComplete avant
d’attribut de registre Modèle de thread de revenir de chacune des méthodes.
et pour les composants COM non ré-
entrants. L’exécution des méthodes est
sérialisée entre tous les objets d’un
composant et entre tous les
composants d’un processus.
Apartment (ou Chaque objet est assigné à un thread Apporte une nette amélioration des
apartment à thread apartment, dont la durée équivaut à la accès simultanés par rapport au modèle
unique) durée de vie de l’objet ; cependant, de thread unique.
plusieurs threads peuvent être utilisés Deux objets peuvent s’exécuter
pour plusieurs objets. C’est le modèle simultanément du moment qu’ils
concurrentiel COM standard. Chaque appartiennent à des activités différentes.
apartment est lié à un thread Ces objets peuvent être dans le même
spécifique et possède une pompe à composant ou dans des composants
messages Windows. différents.
Semblable à l’apartment COM, mais les
objets peuvent être distribués dans
plusieurs processus.

Remarque Ces modèles de threads sont semblables à ceux définis par les objets COM.
Cependant, comme l’environnement MTS fournit un support des threads plus
avantageux, la signification de chaque modèle de thread diffère. D’autre part, le
modèle libre ne s’applique pas aux objets s’exécutant dans l’environnement MTS
à cause de la façon dont MTS supporte les activités.

Activités MTS
MTS supporte les accès simultanés via les activités. Tout objet MTS appartient à
une activité ; elle est enregistrée dans le contexte de l’objet. L’association entre un
objet et une activité ne peut être modifiée. Une activité comprend l’objet MTS
créé par le client de base, ainsi que tous les objets MTS créés par cet objet et ses
descendants. Ces objets peuvent être distribués dans un ou plusieurs processus
s’exécutant sur un ou plusieurs ordinateurs.

50-18 Guide du développeur


Définition de l’attribut transactionnel

Par exemple, une application destinée à des médecins peut posséder un objet
MTS ajoutant des mises à jour et supprimant des enregistrements dans plusieurs
bases de données médicales, chacune représentée par un objet différent. Cet objet
d’ajout peut utiliser également d’autres objets, comme un objet de réception qui
enregistre la transaction. Il en résulte plusieurs objets MTS qui sont, soit
directement, soit indirectement, sous le contrôle du client de base. Ces objets
appartiennent tous à la même activité.
MTS suit le flux d’exécution dans chaque activité, en empêchant qu’un
phénomène de parallélisme n’altère malencontreusement l’état de l’application.
Le résultat de cette fonctionnalité est l’unicité du thread logique d’exécution pour
toute une série d’objets potentiellement distribuables. Grâce à ce thread logique
unique, les applications sont nettement plus faciles à écrire.
Lorsqu’un objet MTS est créé à partir d’un contexte existant, à partir d’un objet
de contexte transactionnel ou d’un objet de contexte MTS, le nouvel objet devient
membre de la même activité. Autrement dit, le nouveau contexte hérite de
l’identificateur d’activité du contexte utilisé pour le créer.
MTS n’autorise qu’un seul thread logique d’exécution pour une même activité.
Ce comportement est semblable à celui d’un apartment COM, mais les objets
peuvent être distribués dans plusieurs processus. Lorsqu’un client de base fait
appel à une activité, toutes les autres demandes de travail dans cette activité (par
exemple, celles provenant d’un autre thread client) sont bloquées jusqu’à ce que
le thread d’exécution initial revienne vers le client.
Pour plus d’informations sur les threads en l’environnement MTS, recherchez
dans la documentation MTS la rubrique "Components and Threading".

Définition de l’attribut transactionnel


Vous pouvez définir l’attribut transactionnel au moment de la conception comme
au moment de l’exécution.
Au moment de la conception, l’expert objet MTS vous demande de choisir un
attribut transactionnel.
Vous pouvez modifier l’attribut transactionnel au moment de l’exécution en
utilisant l’éditeur de bibliothèques de types.
Pour modifier un attribut transactionnel au moment de l’exécution,
1 Choisissez Voir|Bibliothèque de types pour ouvrir l’éditeur de bibliothèques
de types.
2 Sélectionnez la classe correspondant à l’objet MTS.
3 Cliquez sur l’onglet Transaction et choisissez l’attribut transactionnel souhaité.
Remarque Si l’objet MTS est déjà installé dans l’environnement d’exécution, vous devez
d’abord le désinstaller, puis le réinstaller. Pour cela, utilisez Exécuter|Installer les
objets MTS.
Vous pouvez également modifier l’attribut transactionnel d’un objet installé dans
l’environnement d’exécution MTS en utilisant l’explorateur MTS.

Création des objets MTS 50-19


Transmission de références à des objets

Transmission de références à des objets


Vous pouvez passer des références à un objet, par exemple, pour les utiliser en
tant que callback, uniquement par un des moyens suivants :
• Avec le retour d’une interface de création d’un objet, comme CoCreateInstance
(ou son équivalent), ITransactionContext.CreateInstance ou
IObjectContext.CreateInstance.
• Avec un appel à QueryInterface.
• Avec une méthode ayant appelé SafeRef pour obtenir la référence de l’objet.
Une référence à un objet obtenue par un des moyens précédents est appelée
référence sécurisée. MTS garantit que les méthodes invoquées en utilisant des
références sécurisées s’exécuteront dans le contexte convenable.
Les appels qui utilisent des références sécurisées sont toujours transmis à
l’environnement d’exécution MTS. Cela permet à MTS de gérer les options de
contexte, et aux objets MTS d’avoir des durées de vie indépendantes des
références client.

Utilisation de la méthode SafeRef


Un objet peut utiliser la fonction SafeRef pour obtenir une référence sécurisée sur
lui-même afin de la transmettre hors de son contexte.
La fonction SafeRef est définie dans l’unité Mtx.
En entrée, SafeRef utilise
• Une référence à l’ID de l’interface (RIID) que l’objet en cours veut transmettre
à un autre objet ou à un client.
• Une référence à l’interface IUnknown de l’objet en cours.
SafeRef renvoie un pointeur sur l’interface spécifiée par le paramètre RIID, qui est
sécurisé pour passer hors du contexte de l’objet en cours. Elle renvoie nil si
l’objet demande une référence sécurisée sur un objet autre que lui-même, ou si
l’interface requise par le paramètre RIID n’est pas implémentée.
Lorsqu’un objet MTS souhaite transmettre une auto-référence vers un client ou vers
un autre objet (par exemple, pour l’utiliser en tant que callback), il doit toujours
appeler SafeRef d’abord et passer ensuite la référence renvoyée par cet appel. Un
objet ne doit jamais passer de pointeur self, ni d’auto-référence obtenue par un
appel interne à QueryInterface, à un client ou à tout autre objet. Une fois une telle
référence transmise hors du contexte de l’objet, ce n’est plus une référence valide.
Appeler SafeRef sur une référence déjà sécurisée renvoie la référence sécurisée
inchangée, mais le compteur de références sur l’interface est incrémenté.
Lorsqu’un client appelle QueryInterface sur une référence sécurisée, MTS assure
automatiquement que la référence renvoyée au client est également sécurisée.

50-20 Guide du développeur


Définition d’un objet transaction côté client

Un objet qui obtient une référence sécurisée doit libérer la référence sécurisée
lorsqu’elle ne lui est plus nécessaire.
Pour plus de détails concernant SafeRef, voir la rubrique SafeRef de la
documentation MTS.

Callbacks
Les objets peuvent faire des callbacks aux clients et aux autres composants MTS.
Par exemple, vous pouvez avoir un objet qui crée un autre objet. L’objet qui créé
peut transmettre à l’objet créé une référence à lui-même ; l’objet créé peut ensuite
utiliser cette référence pour appeler l’objet qui l’a créé.
Si vous choisissez d’utiliser les callbacks, prenez garde aux restrictions suivantes :
• Un callback au client de base ou à un autre paquet nécessite, sur le client, une
sécurité au niveau des accès. De plus, le client doit être un serveur DCOM.
• L’introduction de coupe-feux doit bloquer les callbacks vers le client.
• Le travail effectué par le callback s’exécute dans l’environnement de l’objet qui
est appelé. Il peut faire partie de la même transaction, d’une transaction
différente, ou d’aucune transaction.
• L’objet qui crée doit appeler SafeRef et transmettre la référence renvoyée à l’objet
créé en vue de se rappeler lui-même.

Définition d’un objet transaction côté client


Une application client de base peut contrôler le contexte transactionnel via
l’interface ITransactionContextEx. L’exemple de code suivant montre une
application client qui utilise CreateTransactionContextEx pour créer le contexte
transactionnel. Cette méthode renvoie une interface vers cet objet.
Cet exemple enveloppe l’appel au contexte transactionnel dans un appel à
OleCheck, nécessaire car la méthode CreateInstance n’est pas déclarée safecall.
procedure TForm1.MoveMoneyClick(Sender: TObject);
begin
Transfer(CLASS_AccountA, CLASS_AccountB, 100);
end;
procedure TForm1.Transfer(DebitAccountId, CreditAccountId: TGuid; Amount: Currency);
var
TransactionContextEx: ITransactionContextEx;
CreditAccountIntf, DebitAccountIntf: IAccount;
begin
TransactionContextEx := CreateTransactionContextEx;
try
OleCheck(TransactionContextEx.CreateInstance(DebitAccountId,
IAccount, DebitAccountIntf));
OleCheck(TransactionContextEx.CreateInstance(CreditAccountId,
IAccount, CreditAccountIntf));
DebitAccountIntf.Debit(Amount);
CreditAccountIntf.Credit(Amount);

Création des objets MTS 50-21


Définition d’un objet transaction côté serveur

except
TransactionContextEx.Abort;
raise;
end;
TransactionContextEx.Commit;
end;

Définition d’un objet transaction côté serveur


Pour contrôler le contexte transactionnel depuis le serveur MTS, créez une
instance d’ObjectContext. Dans l’exemple suivant, la méthode Transfer est dans
l’objet MTS. En utilisant ObjectContext de cette façon, l’instance de l’objet que
nous sommes en train de créer héritera de tous les attributs transactionnels de
l’objet qui le crée. Nous enveloppons l’appel dans un appel à OleCheck car la
méthode CreateInstance n’est pas déclarée safecall.
procedure TAccountTransfer.Transfer(DebitAccountId, CreditAccountId: TGuid;
Amount: Currency);
var
CreditAccountIntf, DebitAccountIntf: IAccount;
begin
try
OleCheck(ObjectContext.CreateInstance(DebitAccountId,
IAccount, DebitAccountIntf));
OleCheck(ObjectContext.CreateInstance(CreditAccountId,
IAccount, CreditAccountIntf));
DebitAccountIntf.Debit(Amount);
CreditAccountIntf.Credit(Amount);
except
DisableCommit;
raise;
end;
EnableCommit;
end;

Débogage et test des objets MTS


Vous pouvez déboguer les objets MTS locaux et distants. Lors du débogage des
objets MTS, vous pouvez désactiver la temporisation des transactions.
La temporisation des transactions définit pendant combien de temps (exprimé en
secondes) une transaction peut rester active. Les transactions toujours vivantes
après le délai de temporisation sont automatiquement annulées par le système.
Par défaut, cette valeur est de 60 secondes. Vous pouvez désactiver la
temporisation des transactions en spécifiant la valeur 0 ; cela peut être utile lors
du débogage des objets MTS.
Pour plus d’informations sur le débogage distant, voir la rubrique Débogage
distant dans l’aide en ligne.

50-22 Guide du développeur


Installation des objets MTS dans un paquet MTS

Lors des tests de l’objet MTS, vous pouvez commencer par le tester hors de
l’environnement MTS, afin que votre environnement de test soit plus simple.
Lorsque vous développez un serveur MTS, vous ne pouvez pas le reconstruire
lorsqu’il est encore en mémoire. Il se peut que vous obteniez une erreur du
compilateur, du style “Impossible d’écrire dans une DLL alors que l’exécutable
est chargé”. Pour l’éviter, vous pouvez définir les propriétés du paquet dans
l’explorateur MTS pour arrêter le serveur lorsqu’il est inactif.
Pour arrêter le serveur MTS lorsqu’il est inactif,
1 Dans l’explorateur MTS, cliquez avec le bouton droit sur le paquet dans lequel
votre composant MTS est installé et choisissez Propriétés.
2 Sélectionnez l’onglet Avancé.
L’onglet Avancé détermine si le processus serveur associé à un paquet
s’exécute toujours, ou s’il s’arrête après un certain temps.
3 Changez la valeur de temporisation en 0, ce qui arrête le serveur dès qu’il n’a
plus de client à servir.
4 Cliquez sur OK pour enregistrer cette option et revenir à l’explorateur MTS.
Remarque Lors des tests hors de l’environnement MTS, vous ne pouvez pas faire de référence
directe à l’ObjectProperty de TMtsObject. Le TMtsObject implémente des méthodes,
comme SetComplete et SetAbort, dont l’appel est sécurisé lorsque le contexte de l’objet
est nil.

Installation des objets MTS dans un paquet MTS


Les applications MTS consistent en un groupe d’objets MTS en processus (ou
modules de données MTS distants) s’exécutant dans une instance unique de
l’exécutable MTS (EXE). Un groupe d’objets COM s’exécutant tous dans le même
processus est appelé un paquet. Sur une machine unique peuvent s’exécuter
plusieurs paquets différents, chaque paquet s’exécutant dans un EXE MTS
séparé.
Vous pouvez grouper les composants de votre application dans un seul paquet
afin qu’ils s’exécutent dans un seul processus. Vous pouvez aussi distribuer vos
composants dans des paquets différents pour partager votre application entre
plusieurs processus ou plusieurs machines.
Pour installer dans un paquet des objets MTS,
1 Choisissez Exécuter|Installer les objets MTS pour installer dans un paquet des
objets MTS.
2 Cochez les objets MTS à installer.
3 Dans la boîte de dialogue Installer l’objet, choisissez Dans nouveau paquet
pour créer le paquet où installer l’objet MTS, ou choisissez Dans un paquet
existant pour installer l’objet dans un des paquets MTS existants présentés
dans la liste.

Création des objets MTS 50-23


Administration des objets MTS avec l’explorateur MTS

4 Choisissez OK pour rafraîchir le catalogue MTS, ce qui rend les objets


accessibles lors de l’exécution.
Les paquets peuvent contenir des composants issus de plusieurs DLL, et des
composants issus de la même DLL peuvent être installés dans différents
paquets. Cependant, un même composant ne peut être distribué entre plusieurs
paquets.

Administration des objets MTS avec l’explorateur


MTS
Une fois les objets MTS installés dans un environnement d’exécution, vous
pouvez administrer ces objets d’exécution en utilisant l’explorateur MTS.
L’explorateur MTS est une interface utilisateur graphique permettant de gérer et
de déployer les composants MTS. Avec l’explorateur MTS, vous pouvez
• Configurer les objets MTS, paquets et rôles
• Voir les propriétés des composants d’un paquet et voir les paquets installés
sur un ordinateur
• Surveiller et gérer les transactions pour les composants MTS qui en
comportent
• Déplacer les paquets entre ordinateurs
• Rendre un objet MTS distant disponible à un client local
Pour plus de détails concernant l’explorateur MTS, voir le guide de l’administrateur
MTS.

Utilisation de la documentation MTS


La documentation accompagnant Microsoft MTS fournit de nombreux détails sur
les concepts MTS, les scénarios de programmation et les outils d’administration.
Cette documentation sera très utile à ceux qui débutent dans le développement
des applications MTS.
Voici la description des documents qui accompagnent le produit Microsoft.

Tableau 50.4 Organisation de la documentation Microsoft MTS


Source Description
Setting Up MTS Décrit comment définir MTS et les composants MTS, en incluant
les instructions d’accès aux bases de données Oracle depuis les
applications MTS et d’installation des exemples d’applications
MTS.
Getting Started with MTS Fournit une vue générale des nouvelles fonctionnalités MTS,
présente rapidement la documentation et contient un glossaire.
Quick Tour of MTS Fournit une présentation générale de MTS.

50-24 Guide du développeur


Utilisation de la documentation MTS

Tableau 50.4 Organisation de la documentation Microsoft MTS


Source Description
MTS Administrator's Guide
Roadmap to the MTS Décrit les différentes façons d’utiliser l’explorateur MTS pour
Administrator’s Guide déployer et administrer des applications et donne une vue
générale de l’interface graphique de l’explorateur MTS.
Creating MTS Packages Fournit une documentation établie par rapport aux tâches sur la
création et l’assemblage des paquets MTS.
Distributing MTS Fournit une documentation établie par rapport aux tâches sur la
Packages distribution des paquets MTS.
Installing MTS Packages Fournit une documentation établie par rapport aux tâches sur
l’installation et la configuration des paquets MTS.
Maintaining MTS Fournit des informations établies par rapport aux tâches sur la
Packages maintenance et la surveillance des paquets MTS.
Managing MTS Décrit les transactions distribuées et la gestion des transactions
Transactions utilisant l’explorateur MTS.
Automating MTS Fournit une vue générale conceptuelle, des procédures et des
Administration exemples de code expliquant comment utiliser les objets MTS
auxquels peuvent s’appliquer des scripts pour automatiser les
procédures dans l’explorateur MTS.
MTS Programmer's Guide
Overview and Concepts Fournit une vue générale du produit et explique comment les
composants de celui-ci fonctionnent ensemble, comment MTS
répond aux besoins des développeurs client/serveur comme des
administrateurs de systèmes, et traite en profondeur des
concepts de programmation des composants MTS.
Building Applications Fournit des informations établies par rapport aux tâches sur le
for MTS développement de composants ActiveX™ pour MTS.
MTS Administrative Fournit une référence pour l’utilisation des objets MTS auxquels
Reference peuvent s’appliquer des scripts pour automatiser les procédures
dans l’explorateur MTS.
MTS Reference Fournit une référence pour l’interface de programmation des
applications MTS (API).

Création des objets MTS 50-25


50-26 Guide du développeur
Chapitre

Création d’une page Active


Chapter 51
51
Server
Si vous utilisez l’environnement Microsoft Internet Information Server (IIS) pour
servir des pages Web, vous pouvez utiliser ASP (Page Active Server) pour créer
des applications dynamiques client Web-serveur. ASP vous permet d’incorporer
dans une page Web des contrôles qui sont appelés à chaque fois que le serveur
charge la page Web. Vous pouvez, par exemple, écrire un serveur Automation
Delphi pour créer un bitmap ou se connecter à une base de données, ce contrôle
accède à des données actualisées à chaque fois que le serveur charge la page
Web.
ASP permet la conception d’applications Web en utilisant des composants
serveur ActiveX (objets Automation). Ce sont des composants côté serveur que
vous pouvez développer en utilisant Delphi ou d’autres langages dont le C++,
Java ou Visual Basic. Du côté client, l’ASP est un document HTML standard qui
peut être visualisé par les clients sur toute plate-forme en utilisant un navigateur
Web.
Avant cette technologie, les applications client devaient être installées sur chaque
système accédant au serveur. Avec le modèle ASP, l’essentiel de l’application
client est exécutée sur le serveur, seule la présentation de l’interface utilisateur
étant exécutée sur le client. Cela simplifie l’actualisation des clients quand une
application est modifiée.
Vous pouvez également utiliser ASP en association avec le serveur de
transactions Microsoft (MTS) pour automatiser la gestion des composants serveur
COM. Pour des détails sur MTS, voir chapitre 50, “Création des objets MTS.”
Ce chapitre décrit la création d’une application ASP en utilisant l’expert ASP
Delphi. Avec son aide, vous pouvez exposer les propriétés et méthodes d’une
application existante dans un contrôle Automation.

Création d’une page Active Server 51-1


Création d’un objet ASP

Voici les étapes de la création d’un objet ASP à partir d’une application
existante :
• Création d’un objet ASP pour l’application.
• Exposition des propriétés et méthodes de l’application pour l’Automation.
• Recensement de l’application comme objet ASP.
• Test et débogage de l’application.
Pour davantage d’informations sur les technologies COM, voir chapitre 44,
“Présentation des technologies COM.” Pour des informations sur la création d’un
contrôleur d’Automation, voir le chapitre 46, “Création d’un contrôleur
Automation.”

Création d’un objet ASP


Un objet ASP est un objet Automation qui accède aux interfaces d’une demande
Web. Comme les autres objets Automation, c’est une classe Pascal Objet dérivée
de TAutoObject qui gère les protocoles Automation et s’expose elle-même pour
pouvoir être utilisée par d’autres applications. Pour créer un objet ASP, utilisez
l’expert d’objet Active Server.
Avant de créer un objet ASP, créez ou ouvrez le projet de l’application contenant
les fonctionnalités à exposer. Le projet peut être, selon vos besoins, une
application ou une bibliothèque ActiveX.
Vous pouvez utiliser Server.CreateObject dans une page ASP pour démarrer,
selon vos besoins, un serveur en ou hors processus. Néanmoins, vous devez tenir
compte des problèmes liés au démarrage d’un serveur hors processus.
Pour afficher l’expert d’objet Active Server :
1 Choisissez Fichier|Nouveau.
2 Sélectionnez l’onglet ActiveX.
3 Double-cliquez sur l’icône Objet Active Server.
Dans l’expert, spécifiez les options suivantes :

Nom de CoClasse Spécifie la classe dont les propriétés et méthodes sont


exposées aux applications client. Delphi préfixe ce nom par
un T.
Instancie Spécifiez un mode d’instanciation qui indique comment
l’objet ASP est démarré. Pour davantage d’informations,
voir “Types d’instanciation des objets COM” à la page 45-3.
Remarque : si l’objet ASP est utilisé uniquement comme
serveur en processus, l’instanciation n’est pas prise en
compte.

51-2 Guide du développeur


Création d’un objet ASP

Modèle de thread Choisissez le modèle de thread pour indiquer comment les


applications client peuvent appeler l’interface de votre
objet. C’est le modèle de thread que vous validez pour
implémenter l’objet ASP. Pour davantage d’informations
sur les modèles de thread, voir “Choix d’un modèle de
thread” à la page 45-4.
Remarque : le modèle de thread choisi détermine comment
l’objet est recensé. Vous devez vous assurer que
l’implémentation de votre objet est conforme au modèle
choisi.
Type d’Active Server Choisissez Méthodes d’événement de niveau page avec IIS
3 et IIS 4. Cela crée un objet ASP qui implémente les
méthodes OnStartPage et OnEndPage. Ces méthodes sont
appelées par le serveur Web quand l’exécution de la page
est initialisée et terminée.
Choisissez Contexte d’objet avec IIS 5. Cela utilise les
fonctionnalités MTS pour obtenir la bonne instance des
données de votre objet.
Générer un script de Génère une page .ASP simple qui crée l’objet Delphi à
test pour cet objet partir de son ProgID.
Générer le code Cochez cette case pour demander à l’expert d’implémenter
de support une interface distincte pour la gestion des événements de
d’événements votre objet ASP. Pour davantage d’informations sur la
gestion des événements, voir “Gestion des événements de
l’objet Automation” à la page 47-3.

Une fois ceci fait, une nouvelle unité est ajoutée au projet en cours qui contient
la définition de l’objet ASP. De plus, l’expert ajoute un projet de bibliothèque de
types. Vous pouvez maintenant exposer les propriétés et méthodes de l’interface
via la bibliothèque de types, comme décrit dans la section “Exposition des
propriétés, méthodes et événements d’une application” à la page 47-3.
Comme tous les objets Automation, l’objet ASP implémente une interface duale
qui gère la liaison précoce (à la compilation) grâce à la VTable et la liaison
tardive (à l’exécution) via l’interface IDispatch. Pour davantage d’informations sur
les interfaces duales, voir “Interfaces duales” à la page 47-7.

Création d’ASP pour des serveurs en et hors


processus
Vous pouvez utiliser Server.CreateObject dans une page ASP pour démarrer un
serveur en processus ou un serveur hors processus selon vos besoins. Cependant
le démarrage de serveurs en processus est plus courant.
Les composants DLL en processus sont plus rapides, plus fiables et peuvent être
hébergés par MTS, ils sont donc mieux adaptés à une utilisation côté serveur.

Création d’une page Active Server 51-3


Recensement d’une application comme objet ASP

Comme les serveurs hors processus sont moins fiables, il est habituel de
configurer IIS pour interdire l’utilisation d’exécutables hors processus. Dans ce
cas, vous recevez un message d’erreur de la forme suivante :
Server object error 'ASP 0196'
Cannot launch out of process component
/path/outofprocess_exe.asp, line 11
De plus, comme les composants hors processus créent souvent des processus de
serveur individuels pour chaque instance d’objet, ils sont plus lents que les
applications CGI. Ils ne supportent pas aussi bien les redéploiements que les
composants DLL qui s’exécutent en processus dans IIS ou MTS. Si les
performances et le redéploiement sont des priorités, il est conseillé de ne pas
utiliser de composants hors processus.
Cependant les sites intranet qui ont un traffic faible ou modéré peuvent utiliser
un composant hors processus sans affecter les performances globales du site.
Pour des informations générales sur les serveurs en et hors processus, voir ,
“Serveurs en processus, hors processus et distants,” on page 44-6.

Recensement d’une application comme objet ASP


Vous pouvez recenser l’objet ASP comme serveur en ou hors processus. Mais les
serveurs en processus sont plus fréquemment utilisés.
Remarque Si vous voulez retirer l’objet ASP de votre système, il est conseillé de commencer
par annuler son recensement, puis de retirer ses entrées dans le registre Windows.

Recensement d’un serveur en processus


Pour recenser un serveur en processus (DLL ou OCX) :
• Choisissez Exécuter|Recenser le serveur ActiveX.
Pour annuler le recensement d’un serveur en processus :
• Choisissez Exécuter|Dérecenser le serveur ActiveX.

Recensement d’un serveur hors processus


Pour recenser un serveur hors processus :
• Exécutez le serveur avec l’option de ligne de commande /regserver. Pour
spécifier les options de la ligne de commande, vous pouvez utiliser la boîte de
dialogue Exécuter|Paramètres.
Vous pouvez également recenser le serveur en l’exécutant.
Pour annuler le recensement d’un serveur hors processus :
• Exécutez le serveur avec l’option de ligne de commande /unregserver.

51-4 Guide du développeur


Test et débogage d’une application ASP

Test et débogage d’une application ASP


Le débogage d’un serveur en processus comme l’objet ASP se fait comme celui
d’une DLL. Vous choisissez une application hôte qui charge la DLL et débogez
de manière habituelle. Pour tester et déboguer un objet ASP :
1 Activez, si nécessaire, les informations de débogage en utilisant la page
Compilateur de la boîte de dialogue Projet|Options. Activez également le
débogage intégré dans la boîte de dialogue Outils|Options du débogueur.
2 Choisissez Exécuter|Paramètres, entrez le nom de votre serveur Web dans la
zone Application hôte et choisissez OK.
3 Choisissez Exécuter|Exécuter.
4 Définissez des points d’arrêt dans l’objet ASP.
5 Utilisez le navigateur Web pour interragir avec l’objet ASP.
L’objet ASP s’arrête quand les points d’arrêt sont atteints.

Création d’une page Active Server 51-5


51-6 Guide du développeur
Index
Symboles sessions 16-5
tables 20-4
création de tables 13-15
restructuration de tables 13-15
& (et commercial), 2-12 ActiveFlag, propriété 27-22 SQL 23-28
caractère 5-21 ActiveForms 48-7 adresses
en tant qu’application MIDAS connexions par socket 30-3,
A Web 14-34
experts 48-7
30-4
adresses IP
abandon des transactions 13-8 licences 48-5, 48-8 Smart Agents 28-21
Abort, méthode 18-30 ActiveX 44-1, 44-12, 48-1 systèmes hôtes à localisations
AbortOnKeyViol, applications Web 44-13 multiples 28-21
propriété 20-26 ActiveX , page de la palette des ADT, champs 19-27, 19-28–
AbortOnProblem, composants 2-11 19-29
propriété 20-26 ActiveX, contrôles 11-3 ADTG 23-18
About, unité 43-3 applications multiniveaux affectation des touches 10-8
AboutDlg, unité 43-2 et 14-33 affectations 2-8
abstract, classes 31-3 de la VCL contrôles 48-3 Affecter données locales,
accès aux données, composants incorporation dans le commande 13-19, 24-14
threads 8-5 document HTML 29-20 affichage des données
AccèsBD, page de la palette des ActnList, unité 5-40, 5-48 dans les grilles 26-18
composants 2-11, 12-2 actualisation des données valeurs en cours 26-10
Access, tables 13-9 applications affichage des valeurs de
niveaux d’isolement 13-9 multiniveaux 15-6, 15-8– champs 19-21
transactions locales 13-10 15-10 AfterApplyUpdates,
Acquire, méthode 8-7 filtrage 15-9 événement 15-6
Action, propriété 5-41 identification des AfterClose, événement 18-6
actions 5-39–5-48 tables 15-10 AfterConnect, événement 14-27
actualisation 5-44 paquets delta 15-7
AfterDisconnect,
centralisation 5-39, 5-41 actualisation des événement 14-27
cibles 5-39 enregistrements 15-6 AfterDispatch,
clients 5-39 Add to Interface, événement 29-10, 29-13
démos 5-48 commande 28-7 AfterGetRecords,
ensembles de données 5-45 Add, méthode événement 15-6
exécution 5-42 colonnes persistantes 26-21
agentaddr, fichier 28-21
fenêtre standard 5-45 listes de chaînes 2-36
menus 5-29 agrégation
prédéfinies 5-44 interfaces 3-21
présentation 5-39 requêtes 21-8
AddAlias, méthode 16-12 agrégats maintenus 12-15,
recensement 5-48
AddFontResource, 24-11–24-14
standard d’éditiont 5-44
sous-totaux 24-13
standard des fenêtres 5-45 fonction 11-11
aide 2-24
Update, méthode 5-44 AddObject, méthode 2-37
aide à la décision 12-15
utilisation 5-41 AddRef, méthode 3-18, 3-22,
Actions, propriété 29-9 aide contextuelle 2-24
3-24
activation des mises à jour en IUnknown 44-4 aide en ligne 38-4
mémoire cache 25-3 Address, propriété ajout d’enregistrements
client, sockets 30-6 opérations groupées 20-21,
activation just-in-time 50-5
20-24
Active, propriété 23-26 TSocketConnection 14-23
AddStandardAlias, Ajout de champs, boîte de
client, sockets 30-6
composants connexion méthode 16-12 dialogue 19-6
ADO 23-5 AddStrings, méthode Ajouter à l’interface,
ensembles de données 18-3, listes de chaînes 2-36, 2-37 commande 14-19
18-6 ADO 2.1 13-13 Ajouter au référentiel,
requêtes 21-13 applications 13-12 commande 2-41
serveur, sockets 30-7

Index I-1
algorithmes application, serveurs 12-11 création 14-21–14-28
cryptographiques 48-27 Application, variable 5-3, 29-7 extraction de données 21-3
alias applications fourniture de requêtes 24-20
connexions de base de annulation des mises à jour en mémoire cache
données 16-7 modifications 18-27 et 25-2
connexions distantes 17-9 base de données 12-1 multiniveaux
éditeur de bibliothèques de CGI 4-12 mise à jour des
types 49-19, 49-30 client/serveur 14-1, 17-2 enregistrements 24-24–
moteur de bases de protocoles réseau 17-8 24-27
données 16-11–16-12 COM 4-12, 44-3, 44-18 régularisation des mises à
noms de base de données COM et CORBA jour 24-26
et 13-3 combinés 28-5 protocoles réseau 17-8
sessions et 13-4 CORBA 4-13, 28-1 rafraîchissement des
spécification 17-5, 17-6 distribuées 4-10––4-13 enregistrements 24-27–24-28
suppression 16-12 threads et 8-12 simples 14-33
AliasName, propriété 17-5 graphiquesl 31-8, 36-1 applications client simples 14-2
Align, propriété 2-23, 5-4 ISAPI 4-12, 29-5, 29-6 applications client/serveur 4-10
contrôles texte 6-7 MDI 4-2 applications CORBA 28-1
volets 5-32 MIDAS Web 14-32–14-44 clients 28-13
Alignment, propriété 2-14, 2-16, MTS 4-12 déploiement 28-18–28-22
2-23, 19-3 multiniveaux 14-1 serveurs 28-5–28-12
en-têtes de colonne 26-25 multithread 8-1, 16-3, 16-17– serveurs COM et CORBA
grilles de décision 27-14 16-19 combinés 28-5
grilles de données 26-24 distribuées 8-12 vue générale 28-2
mémo, champs 26-11 NSAPI 4-12, 29-5, 29-6 applications de base de
valeurs de champ 19-15 optimisation des données 11-4, 12-1
AllowAllUp, propriété 2-18 recherches 20-6 architecture 12-7, 14-32
boutons outils 5-36 réalisation de palettes 36-5, basées sur le BDE ou
turboboutons 5-34 36-6 linéaires 13-16
AllowDelete, propriété 26-32 SDI 4-2 distribuées 4-13
AllowGrayed, propriété 2-18 serveur Web 4-11 évolution 12-8, 13-21
AllowInsert, propriété 26-32 service 4-3 fichier linéraire 13-15–13-19
synchronisation des applications distantes
alTop, constante 5-32
tables 20-27 extraction de données 21-1,
Analyser Params,
Win-CGI 4-12 21-3, 21-16, 21-19
commande 24-18
applications à niveau mises à jour en mémoire cache
années bissextiles 41-8
double 12-3, 12-8, 12-10, 13-1– et 25-1
annulation des mises à jour en opérations groupées 20-24
13-12
mémoire cache 25-9–25-10 ou applications à niveau TCP/IP 30-1
annulation des unique 13-3 applications distribuées 4-10
modifications 18-27 applications à niveau base de données 4-13
ANSI unique 12-3, 12-8, 12-10, 13-1– COM 4-12
jeu de caractères 10-2 CORBA 4-13, 28-1
13-19
chaînes standard 3-26 MTS 4-12
basées sur le BDE ou
AnsiChar 3-25 linéaires 13-16 multithread 8-12
AnsiString 3-28 fichier linéraire 13-15–13-19 applications internationales 10-1
apartment, modèle de ou applications à niveau abréviations et 10-9
thread 45-6 double 13-3 conversion des saisies
Append, méthode 18-8, 18-26 applications ADO 13-12 clavier 10-8
Insert et 18-25 applications bidirectionnelles localisation 10-12
AppendRecord, méthode 18-28 méthodes 10-7 applications linéaires 13-15–
application à niveau triple Voir propriétés 10-5 13-19
applications multiniveaux applications client mémoire 13-15
application client base de données 17-2 applications MDI 4-1
instructions SQL 15-4 comme applications serveur applications multiniveaux 12-3,
application de répartition de Web 14-32 12-8, 12-11, 14-1
sockets 14-14, 14-23 CORBA 28-2, 28-13

I-2 Guide du développeur


applications MIDAS répartiteur Web et 29-9 AS_GetRecords, méthode 14-9
Web 14-32–14-44 standards 29-1 AS_RowRequest, méthode 14-9
architecture 14-5 types 29-5 AsBoolean, fonction 19-22
avantages 14-2 utilisation de MTS pour le ASCII, tables 16-12, 20-3
construction 14-12–14-28 débogage 29-28–29-29 AsCurrency, fonction 19-22
contraintes de données 15-11 applications service 4-3–4-8 AsDateTime, fonction 19-22
définition 14-1 code exemple 4-4, 4-6 AsFloat, fonction 19-22
déploiement 11-6 applications Web AsInteger, fonction 19-22
événements du client 15-11 ActiveX 44-13 ASP
événements générés par le Appliquer les mises à jour, créer 51-1–51-5
client 24-28 boîte de dialogue 49-35 gestion des événements 51-3
extraction de données 24-20 Apply, méthode recenser 51-4
fournisseurs de données 14-19, objets mise à jour 25-21 tester et déboguer 51-5
15-1 ApplyRange, méthode 20-16 utiliser l’expert 51-2
mise à jour des ApplyUpdates, méthode Assign, méthode
enregistrements 24-24–24-27 applications listes de chaînes 2-37
régularisation des mises à multiniveaux 24-24 AssignedValues,
jour 24-26 ensembles de données
rappels 14-20 propriété 26-21
client 24-24 AssignValue, méthode 19-21
transmission de mises à jour en mémoire
paramètres 24-17, 24-18 Associate, propriété 2-16
cache 25-6, 25-7 AsString, fonction 19-22
applications multithread 16-3, sockets 18-34
16-17–16-19 AsVariant, fonction 19-22
AppServer, propriété 14-9, ATL 44-2
distribuées 8-12 14-20, 14-27
applications SDI 4-1 atomicité
Arc, méthode 7-4 transactions 50-8
applications serveur architecture
architecture 14-5 Attributes, propriété 23-7
à niveau double 13-2 attributs
CORBA 28-2, 28-5–28-12 à niveau unique 13-2
interfaces Automation 47-8 éditeurs de propriétés 38-11
applications basées sur le attributs de champ 12-5, 19-17–
multiniveaux 14-5, 14-13–14-21 BDE 13-2
fournisseurs de 19-18
applications CORBA 28-2––
données 14-19 suppression 19-18
28-5
présentation 14-1 attributs de transaction
applications de base de
recensement 14-13, 14-14, 28-9 modules de données
données 12-7
OAD 28-4 MTS 14-17
applications serveur Web 29-8
applications serveur Web 4-11, client, applications 14-4
AutoCalcFields, propriété 18-30
29-1–29-31 multiniveau 14-4, 14-5 AutoComplete, propriété 14-7
accès aux bases de basée sur le Web 14-32 AutoConnect 46-3
données 29-23 serveur, applications 14-5 AutoDisplay, propriété 26-11
ajout aux projets 29-7 architecture ADO 13-12 contrôles d’édition de texte
conversion 29-31 formaté 26-11
architecture Common Object
création 29-6 graphiques 26-12
Request Broker Architecture
création de réponses 29-12 AutoEdit, propriété 26-3, 26-8
débogage 29-27–29-31
Voir CORBA
architecture de base de AutoHotKeys, propriété 5-21
emplacements des Automation
ressources 29-2 données 12-7
compatibilité des types 47-9,
envoi de données aux 29-15 arrêt des threads 8-12
49-24
envoi de fichiers 29-18 arrière-plan 10-9
création d’objets 44-18
gestion d’événements 29-9, transparent 10-9
descriptions des types 44-12,
29-12, 29-13 as, mot réservé 47-8
gestion des connexions de base liaison anticipée 14-28 interfaces 47-7–47-9
de données 29-23 AS_ApplyUpdates, liaison différée 47-8
interrogation de tables 29-26 méthode 14-9 optimisation 44-15
MIDAS 14-34–14-44 AS_DataRequest, méthode 14-9 propriétés 47-7
modèles 29-7 AS_Execute, méthode 14-9 vérification des types 47-7
modèles de réponse 29-19 AS_GetParams, méthode 14-9 AutoPopup, propriété 5-38
présentation 29-5–29-9 AS_GetProviderNames, AutoSelect, propriété 2-14
méthode 14-9

Index I-3
AutoSessionName, application des mises à jour en batUpdate, constante 20-21,
propriété 16-19, 29-23 mémoire cache 25-6 20-23
AutoSize, propriété 5-4, 11-10, applications Web et 29-23 BeforeApplyUpdates,
26-10 association aux sessions 16-3, événement 15-6
.AVI, fichiers 7-34 16-9, 17-4 BeforeClose, événement 18-6,
AVI 2-26 BDE et 13-5 18-27
choix 12-2 BeforeConnect,
connexion 12-4, 13-5, 17-7
B pooling 50-13
événement 14-27
BeforeDisconnect,
balises HTML transparentes création 16-3, 17-3
événement 14-27
conversion 29-20 à l’exécution 17-3
dans les applications
BeforeDispatch,
paramètres 29-19 événement 29-9, 29-12
préféfinies 14-43–14-44, 29-19 multithread 13-5
DatabaseName, propriété, BeforeGetRecords,
syntaxe 29-19 événement 14-31, 15-6
Bands, propriété 2-18, 5-37 et 13-4
décompte 16-13 BeforeUpdateRecord,
barres d’état 2-23 événement 15-10
internationalisation 10-9 dénomination 17-5, 20-2
distantes 12-3 BeginDrag, méthode 6-2
barres d’outils 2-18
enregistrement de BeginRead, méthode 8-8
ajout 5-34–5-36
ajout de volets comme 5-32– données 18-27 BeginTrans, méthode 23-12
5-34 extraction de données 18-19, BeginWrite, méthode 8-8
conception 5-31–5-39 19-21, 25-1 Beveled, propriété 2-16
définition des marges 5-34 fermeture 16-6 bibliothèque COM 44-2
désactivation des boutons 5-35 génération de réponses bibliothèques
insertion de boutons 5-33–5-34, HTML 29-23–29-26 contrôles personnalisés 31-5
5-35 importation de données 20-21 bibliothèques de types 44-10,
masquer 5-38 limitation des extractions de 44-12, 44-13–44-16, 49-1
menus contextuels 5-38 données 20-12 accès 44-15
outil de dessin par locales 12-3 ajout d’interfaces 49-32
défautl 5-34 marquage d’enregistre- ajout de méthodes 49-33
transparentes 5-36, 5-37 ments 18-15–18-16 ajout de propriétés 49-33
barres de défilement 2-15 modèles multiniveaux 14-2 attributs 49-8
fenêtres du texte 6-8–6-9 modification des avantages 44-15
barres de menus données 13-10, 18-24, 18-28 contenu 44-13, 49-2
déplacement d’éléments 5-23 nouveau tri des champs 20-11 contrôles ActiveX 48-3
propriétés d’accès 42-5–42-6 création 44-14, 49-23
barres de progression 2-23
relationnelles 12-1, 15-11, 21-1 création de contrôleurs
barres graduées 2-15
sécurité 12-4 Automation 46-2
barres multiples 2-18, 5-32 sources de données et 26-6
ajout 5-37 déploiement 49-37
suppression de tables 20-18 dérecensement 44-16
conception 5-31–5-39
tables 12-15 enregistrement 49-36
configuration 5-37 changement de nom 20-18
masquer 5-38 exportation d’un fichier
test de l’état 16-5 IDL 49-37
bascules 5-34, 5-36 test des associations 16-9 IDL et ODL 44-14
base de données, transactions 12-5, 13-7 inclusion en tant que
composants 17-3 types 12-2 ressources 45-3, 49-37
base de données temporaire, Basic Object Adaptor informations de type 49-7
composants 16-9, 17-2 (BOA) 28-2, 28-16 navigateurs 44-15
base de registre 2-37 batAppend, constante 20-21, optimisation des
bases de données 4-10, 12-2– 20-23 performances 49-11
12-7, 13-3, 17-1, 17-10, 42-1 batAppendUpdate, outils 44-16
accès aux 16-1, 16-7, 20-2, 21-4 constante 20-21, 20-23 ouverture 49-32
accès non autorisé 17-7 BatchMove, méthode 20-21 page Utilise (éditeur de
ajout de données 18-25–18-26, batCopy, constante 20-21, 20-23 types) 49-8
18-28 précaution 20-21 quand les utiliser 44-14
ajout de tables 20-19 recensement 44-16, 49-36
batDelete, constante 20-21, 20-23
alias et 17-5, 20-2 recensement des objets 44-15

I-4 Guide du développeur


référence à d’autres boîte d’état des threads 8-15 booléennes
bibliothèques de types 49-8 boîte de dialogue A propos valeurs 42-4
types autorisés 49-24 de 43-2, 43-3 BorderStyle, propriété 2-12
vérification de la syntaxe 49-36 ajout de propriétés 43-4 BorderWidth, propriété 2-22
visualisation 44-16 boîtes à options 2-20, 26-2, Borland Database Engine
visualisation dans 26-12, 26-14 connexion aux bases de
l’éditeur 49-8 dessinées par le données 50-6
bibliothèques de types, propriétaire 6-13 boucle des messages
dispinterfaces 49-14 measure-item, événements threads 8-5
bibliothèques javascript 14-34, de type 6-16 boutons 2-16–2-18
14-36–14-37 styles de variant 6-14 affectation de glyphes
emplacement 14-36, 14-37 orientées données 26-13 aux 5-33
biseaux 2-25 boîtes à options de ajout aux barres d’outils 5-33–
bitmap, objet 7-3 référence 26-2, 26-14–26-16 5-34, 5-35
bitmaps 2-25, 36-4 définition des propriétés 26-15 colonnes de grille et 26-24
ajout aux composants 38-4 obtention des valeurs 26-14, désactivation sur les barres
ajout du défilement 7-17 26-15, 26-16 d’outils 5-35
apparition dans boîtes à peindre 2-25 navigateur 26-32
l’application 7-2 boîtes de déilement 2-22 boutons ... (points de
association aux chaînes 6-14 boîtes de dialogue 2-42, 43-1– suspension) 26-24
barres d’outils 5-35 43-7 boutons bitmap 2-17
brushes, propriété 7-8, 7-9 création 43-1 boutons de souris relâchés 7-26
chargement 36-5 définition de l’état initial 43-2 boutons outils 5-35
comparés aux contrôles éditeurs de propriété ajout d’images 5-35
graphiques 40-3 comme 38-10 dans plusieurs lignes 5-36
défilement 7-18 internationalisation 10-9, 10-10 désactivation 5-35
définition de la taille standard Windows 43-1, 43-2 état initial, définition 5-36
initiale 7-18 création 43-2 forcer le passage à la
dessin sur 7-19 exécution 43-5 ligne 5-36
destruction 7-22 boîtes de dialogue groupe/interrompre le
événements dessinés par le standard 2-26, 43-1, 43-2 groupe 5-36
propriétaire 6-17 création 43-2 obtenir de l’aide avec 5-38
hors écran 36-6–36-7 exécution 43-5 utilisation comme
internationalisation 10-10 boîtes graphiques 26-2 bascules 5-36
libération de la mémoire 7-22 boîtes groupe 2-21 boutons radio 2-18, 26-2
pinceaux 7-9 boîtes liste 2-19, 26-2, 26-12, orientés données 26-17
remplacement 7-21 26-14, 41-1 regrouper 2-21
ScanLine, propriété 7-9 déplacement des éléments 6-3 sélection 26-18
surfaces de dessin 36-4 dessinées par le boutons souris 7-25
temporaires 7-18, 7-19 propriétaire 6-13 clic 7-25, 7-26
vides 7-18 draw-item, événements 6-17 événements de déplacement
BLOB 26-10, 26-11 measure-item, événements souris et 7-27
mise en mémoire cache 18-35 de type 6-16 .BPL, fichiers 11-3
BLOB, champs 26-2 styles de variant 6-14 BPL, fichiers 9-1
affichage des graphiques 26-12 faire glisser des éléments 6-2, Brush, propriété 2-25, 7-4, 7-8,
affichage des valeurs 26-10, 6-3 36-3
26-11 orientées données 26-13 BrushCopy, méthode 36-3, 36-7
extraction à la demande 24-21 stockage des propriétés Business Object Broker 11-6
mise à jour 25-4 exemple 5-9
ButtonAutoSize, propriété 27-11
obtention de valeurs 18-35 boîtes liste de référence 26-2, ButtonStyle, propriété 26-23,
BMPDlg, unité 7-21 26-14–26-16 26-24
BOA 28-2, 28-16 définition des propriétés 26-15
By Reference Only, propriété de
BOF, propriété 18-14 obtention des valeurs 26-14,
bibliothèque de types 49-11
Bof, propriété 18-11, 18-12 26-15, 26-16
Bookmark, propriété 18-15 ByteType 3-31
boîte A propos
ActiveForms 48-5 BookmarkValid, méthode 18-16
contrôles ActiveX 48-8

Index I-5
C en-têtes de colonne 26-25
entrées incorrectes 5-19
routines
bibliothèque
.CAB, fichiers 48-20 grilles de décision 27-14 d’exécution 3-29
cabinets (définition) 48-20 caractère & 2-12 distinction minuscules/
CacheBlobs, propriété 18-35 caractères 33-2 majuscules 3-30
CachedUpdates, caractères accentués 10-10 localisation Windows 3-30
propriété 18-34, 25-3 caractères d’espacement support des caractères
exécution de requêtes sur multi-octets 3-31
cadres 5-12, 5-14–5-16
des 21-6 taille 6-9
et modèles de
caractères étendus traduction 10-2, 10-8, 10-10
composants 5-14, 5-15, 5-16
routines de bibliothèque tri 10-10
graphiques 5-16
d’exécution 3-29 tronquer 10-3
partage et distribution 5-16
variables locales 3-34
ressources 5-16 caractères larges 10-3
caractères spéciaux chaînes courtes 3-26
cadres bitmap 2-26
requêtes et 21-6 chaînes de caractères
calendriers 41-1–41-13
ajout de dates 41-5–41-10 carrés, dessin 40-9 larges 47-10
définition des propriétés et CaseInsFields, propriété 13-18 chaînes longues 3-27
événements 41-2, 41-6, 41-11 cases à cocher 2-18, 26-2 champ, objets 19-1
en lecture seule 42-3–42-4 activation 26-16 affectation de valeurs 19-23,
manipulation 41-10–41-13 orientées données 26-16 19-24
redimensionnement 41-4 affichage seulement 19-8
cassettes vidéo 7-34
sélection du jour en cours 41-9 ajout 19-1–19-5, 19-7
cbsAuto, constante 26-23
callback 14-11 dynamiques ou
CDaudio, disques 7-34 persistants 19-3, 19-4
CanBePooled 50-6 CellDrawState, fonction 27-14 événements 19-20
Cancel, méthode 18-7, 18-8, CellRect, méthode 2-24 propriétés 19-3, 19-14–19-19
18-27, 23-6, 23-30 Cells, fonction 27-14 exécution 19-16
Cancel, propriété 2-17 Cells, propriété 2-25 suppression 19-14
CancelRange, méthode 20-17 cellules de grille 2-24 champs 19-1
CancelUpdates, méthode 18-34, CellValueArray, fonction 27-14 activation 19-21
25-9 cercles, dessin 40-9 affectation de valeurs 18-28
canevas 31-7, 36-2, 36-3 CGI, programmes 4-12, 29-4, affichage des valeurs 19-21,
affichage du texte 7-25 29-5, 29-6 26-13
ajout de formes 7-11–7-12, 7-14 création 29-6 ajout aux fiches 7-27–7-28
dessin 7-4 débogage 29-31 bases de données 42-5, 42-7
dessin de lignes 7-10–7-11, chaîne, champs colonnes persistantes et 26-21
7-28–7-30 saisie de données 19-18 définition 19-9, 19-10, 19-11
gestionnaires taille 19-8 définition des limites de
d’événements 7-27 chaînes 3-25, 33-2, 33-9 données 19-25–19-26
lignes dessinées 7-5 association de graphiques 6-14 enregistrements de
changement de la largeur comparaison 18-22 message 37-6
du crayon 7-6 comptage de références 3-27, lecture seule 26-3
outils de dessin par 3-34 mise à jour des valeurs 26-3
défaut 40-5 conversions PChar 3-33 modification des valeurs 26-4
palettes 36-5–36-6 conversions sur 2 octets 10-3 nouveau tri 20-11
présentation 7-1–7-3 corruption de la mémoire 3-36 obtention 19-5, 19-6
propriétés communes, déclaration et obtention de la valeur en
méthodes 7-4 initialisation 3-32 cours 25-31
rafraîchissement de l’écran 7-2 directives de compilation 3-35 obtention des valeurs
CanModify, propriété fichiers 3-43 précédentes 25-12, 25-31
ensembles de données 18-8 jeux de caractères étendus 3-36 options mutuellement
grilles de données 26-28 mélange et conversion de exclusives 26-2
requêtes 21-18 types 3-33 partage de propriétés 19-17
tables 20-5 paramètres variables 3-35 représentation 26-9
Canvas, propriété 2-25, 31-8 position de départ 6-9 saisie des données 18-25,
Caption, propriété 2-12, 2-21, présentation des types 3-26 18-26, 19-18, 26-12–26-16,
2-23 renvoi 33-9 26-17

I-6 Guide du développeur


types de données classes 31-2, 32-1, 33-3 numéros de port 30-5
abstraites 19-27 abstraites 31-3 ouverture 30-6
valeurs par défaut 19-25 accès 32-4–32-7, 40-6 client, ensembles de données
vérification des valeurs en ancêtre 32-4 chargement de données 13-19
cours 19-25 création 32-2 copie de tables
champs agrégat 19-8 définition 31-12, 32-2–32-3 existantes 13-18
champs BLOB méthodes statiques et 32-8 création 13-16, 13-17
lecture à la demande 15-3 méthodes virtuelles et 32-9 création de tables 13-17
champs booléens 26-2, 26-16 dérivation de nouvelles 32-2, enregistrement de
champs cachés 15-3 32-3, 32-9 données 13-19
champs calculés 18-10, 18-30, dérivées 32-9 enregistrement de
19-7 descendantes 32-4, 32-9 modifications 13-19
affectation de valeurs 19-10 éditeurs de propriétés index 13-19
champs de référence et 19-12 comme 38-8 client, requêtes 29-3–29-5
définition 19-10 héritage 32-8 client, sockets 30-3, 30-6–30-7
ensembles de données hiérarchie 32-4 affectation d’hôtes 30-4
client 24-10 instanciation 32-2 connexion aux serveurs 30-9
champs clé 20-16 par défaut 32-4 demande de services 30-6
champs clé secondaires partie protégée 32-6 gestion d’événements 30-9
recherche sur des 20-9 partie publiée 32-7 identification des serveurs 30-6
champs d’agrégat 24-14 partie publique 32-6 messages d’erreur 30-8
champs de référence 19-8, propriétés comme 33-3 propriétés 30-6
transmission comme socket Windows, objets 30-6
19-31, 26-14
paramètres 32-10 utilisation de threads 30-12
attribution de noms aux 26-23
définition 19-11, 26-23 classes ancêtre 2-2, 2-6, 32-4 ClientExecute, méthode
mise en mémoire cache des par défaut 32-4 TServerClientThread 30-14
valeurs 19-12 classes dérivées 2-6 clients de base 50-2, 50-15
champs non indexés classes descendantes 32-4 clients Voir applications client
recherche sur des 20-6 redéfinition des méthodes 32-9 ClientType, propriété 30-11,
champs numériques 19-19 Clear, méthode 19-21, 21-8 30-12
champs persistants 19-4, 26-19 listes de chaînes 2-36, 2-37 Clipboard 6-10
création 19-6, 19-7 ClearSelection, méthode 6-11 objets graphiques 7-3
création d’ensembles de clés test des images 7-23
données client 13-16 définition des portées 20-16 Clipbrd, unité 6-9
modification de l’ordre 19-7 recherche sur des 20-8 clips audio 7-32
paquets de données 15-3 clés, champs clips AVI 7-30, 7-34
suppression 19-14 multiples 20-8, 20-14, 20-15 clips vidéo 7-30, 7-32
champs spécifiques clés partielles clonage de curseurs 24-16
parcours 20-9 définition des portées 20-16 CloneCursor, méthode 24-16
Change, méthode 42-12 recherche sur des 20-8 Close, méthode
ChangedTableName, clic, événements 7-25, 7-26 connexions de base de
propriété 20-26 click, événements 34-8 données 16-8
CHANGEINDEX 24-7 Click, méthode 34-2 ensembles de données 18-6
changements non validés 13-8 surcharge 34-7, 41-11 requêtes 21-8
Char, type de données 3-25, client, applications sessions 16-6
10-2 architecture 14-4 tables 20-4
extraction de données 21-1 CloseDatabase, méthode 16-8
CharCase, propriété 2-14
interfaces 30-2 CLSID 44-5, 44-6, 44-13
chargement multiniveaux 14-1, 14-2, 14-4
graphiques 36-4 CM_EXIT, message 42-12
simples 14-2 CMExit, méthode 42-12
Chart FX 11-3 sockets et 30-1
CHECK, contrainte 15-12 CoClasses 44-6
transactions 13-9 ajout aux bibliothèques de
Checked, propriété 2-18 utilisateur, interfaces 12-11,
CheckOpen, méthode 18-32 types 49-34
14-1 CLSID 44-6
chemins (URL) 29-2 client, connexions 30-2, 30-3
Chord, méthode 7-4 éditeur de bibliothèques de
acceptation de requêtes 30-7 types 49-16, 49-29
instanciation 44-6

Index I-7
code 35-4 identificateurs de composants 2-7, 32-1, 33-3
optimisation 7-15 répartition 47-8 abstraits 31-3
Code Insight implémentation 44-6 aide à la décision 27-1
modèles 4-3 informations de type 44-13 aide en ligne 38-4
code source interfaces doubles 47-7 ajout à l’unité existante 31-11
gestionnaire d’événement 2-29 marshaling 44-8 ajout à la palette des
codes caractères sur deux objets Automation 47-7-47-9 composants 38-1
optimisation 44-15 ajout aux unités 31-11
octets 10-2
pointeur d’interface 44-5 classes dérivées 31-12, 40-2
cohérence présentation 44-1
transactions 50-8 contrôles 2-6
proxy 44-7 création 31-2, 31-8
ColCount, propriété 26-32 spécification 44-2
colonnes 2-24, 26-19 dépendances 31-5–31-6
technologies 44-10 double-clic 38-13, 38-15–38-16
accès aux 19-2 threads et 8-13
affectation de valeurs 26-21, gestion mémoir et 2-10
types de serveurs 44-5 initialisation 33-12, 40-7, 42-6
26-23 COM, objets
état par défaut 26-19, 26-25 installation 2-43, 9-6–9-7, 38-21
gestion de la durée de vie 3-18 interfaces 32-4, 32-6, 43-2
grilles de décision 27-13
COMCTL32.DLL 5-32 conception 32-7
inclusion dans les tables
CommandCount, exécution 32-6
HTML 29-25
modification de valeurs 26-28
propriété 23-10 menus contextuels 38-13,
persistantes 26-18, 26-20–26-21 commandes ADO 13-15 38-14–38-15
ajout de boutons aux 26-24 annuler 23-30 modification 39-1–39-4
création 26-22–26-25 ensembles de résultats 23-30 modifier les données 42-8–
insertion 26-21 exécuter 23-30 42-13
modification de paramètres 23-19, 23-31 MTS 50-2
l’ordre 26-23, 26-29 commandes utilisateur 5-39, non visuels 31-5, 43-3
suppression 26-20, 26-21, 5-41 orientés données 42-1
26-23 Commands, propriété 23-10 palette des bitmaps 38-4
propriétés 26-19, 26-20, 26-24 CommandText, propriété 23-20, paquets 38-21
réinitialisation 26-25 23-21, 23-29, 24-20 personnalisation 2-43, 5-12,
Color, propriété 2-12, 2-25 CommandTimeout, 31-3, 33-1
crayons 7-5, 7-6 propriété 23-7, 23-30 présentation 2-10, 31-1
en-têtes de colonne 26-25 CommandType, propriétaire 2-10
grilles de décision 27-14 propriété 23-21, 23-29 propriétés communes 2-11–
grilles de données 26-24 Commit, méthode 13-7 2-13
pinceaux 7-8 CommitTrans, méthode 23-12 recensement 31-12, 38-2
Cols, propriété 2-25 renommer 2-5–2-6
CommitUpdates,
Columns, propriété 2-19, 2-21, répondre aux événements 42-7
méthode 18-34, 25-7
26-22 ressources, libération 43-5
communication entre scruter les données 42-2–42-7
grilles 26-19 niveaux 14-5, 14-9–14-12,
ColWidths, propriété 2-24, 6-16 standard 2-11
14-22 test 31-13, 43-6–43-7
COM 44-2 CORBA 14-12, 14-25
applications 44-18 composants ADO 23-1–23-31
DCOM 14-10, 14-23 composants commande 23-28
composantes 44-3 HTTP 14-11, 14-24
distribuées 4-12 composants connexion 23-3
OLE 14-25 composants ensemble de
clients 44-3, 44-9 OLEnterprise 14-12
CORBA et 28-1 données 23-12, 23-20
TCP/IP 14-10, 14-23 composants procédure
experts 44-18, 44-19, 45-1, 45-2 communications 30-1
extensions 44-2, 44-9 stockée 23-24
normes composants requête 23-22
comparaison des OMG 28-1
technologies 44-10 composants table 23-21
protocoles 29-1, 30-2 contrôles orientés
interfaces 44-3 connexion,
ajout aux bibliothèques de données 23-14
composants 12-12, 14-5
types 49-32 description 23-2
réseaux 17-8
décompte de références 44-5 ensembles
UDP et TCP 28-3
définition 44-3 d’enregistrements 23-15
standards 29-1
dérivation 44-4 présentation 23-1
composant enveloppe 43-2

I-8 Guide du développeur


composants base de données précaution d’utilisation de fermeture 14-27, 30-8
itération 16-13 MTS 14-8 multiples 30-5
temporaires 16-8 regroupement 14-6 ouverture 14-22, 30-6, 30-7
composants d’accès aux bases de données 12,4 connexions non
données 12-2 pooling 50-13 bloquantes 30-11–30-12
isolement 12-7 client 30-3 connexions bloquantes
composants d’aide à la déconnexion 16-8, 17-9 ou 30-11
décision 27-1, 27-20 composants temporaires connexions par socket 30-2–30-3
ajout 27-3–27-5 et 16-7 envoi/réception
allocation de mémoire 27-21 définition des paramètres 17-6, d’informations 30-11
obtention de valeurs 27-5–27-7 17-7 fermeture 30-7, 30-8
options de conception 27-10 définition du comportement multiples 30-5
présentation 27-1–27-2 par défaut 16-6 ouverture 30-7
composants de connexion 14-3, désactivation 16-6 points de terminaison 30-3,
fermeture 14-27, 30-8 30-5
14-5, 14-22–14-28
gestion 14-26 types 30-2
composants enveloppe MTS 50-4
initialisation 43-3 conseils d’aide 2-24
ouverture 14-22, 14-26, 16-6, console, applications 4-3
composants menu 5-17 16-7, 30-6
composants non visuels 2-31, CGI 29-6
paramètres 17-6 constantes d’état
19-2, 26-6, 31-12 persistantes 16-7, 16-8
compression des données mises à jour en mémoire
protocoles réseau 17-8 cache 25-12
TSocketConnection 14-24 serveurs de bases de
comptage de références constantes d’état de
données 17-7, 17-9
interfaces 3-22–3-24 données 18-4
serveurs distants 17-8–17-9
objets COM 3-18 session 18-32 CONSTRAINT, contrainte 15-12
ComputerName, SQL, serveurs 12-4 ConstraintBroker,
propriété 14-23, 14-25 TCP/IP 30-2–30-3 gestionnaire 11-6
concepteur de menus 2-30, test 16-5 ConstraintErrorMessage,
5-17–5-18, 5-25 Web 14-11 propriété 19-15, 19-26
concepteur de module de connexions abonnées Constraints, propriété 5-4, 15-12
données 2-38–2-40 bases de données constructeur de requêtes 21-7
ConfigMode, propriété 16-11 temporaires 16-9 constructeurs 31-13, 33-11, 35-3,
Connected, propriété 17-7, 23-4 connexions ADO 23-12 41-3, 41-4, 42-6
Connection, propriété 23-14, activer 23-5 multiples 5-8
23-20, 23-21, 23-23, 23-24 identification 23-8 objets ayant un propriétaire
ConnectionObject, optimisation 23-6 et 40-5, 40-7
propriété 23-4 présentation 13-13, 23-3 surcharge 39-2
ConnectionString, procédures stockées Contains, liste (paquets) 9-8,
propriété 23-3, 23-4, 23-8, disponibles 23-11 9-11
23-20, 23-21, 23-23, 23-24 se connecter aux stockages de Content, méthode
ConnectionTimeout, données 23-3 générateurs de page 29-20
propriété 23-7 transactions 23-11 Content, propriété
ConnectOptions, propriété 23-6 connexions bloquantes 30-12– réponse Web, objets 29-18
ConnectTo, méthode 46-3 30-15 ContentFromStream, méthode
connexions non bloquantes générateurs de page 29-20
connexion, boîte de
dialogue 17-7 ou 30-11 ContentFromString, méthode
gestion d’événements 30-10 générateurs de page 29-20
connexion, composants 12-12
connexions d’écoute 30-2, 30-3, ContentStream, propriété
connexions
30-7, 30-10 réponse Web, objets 29-18
abandon
base de données fermeture 30-8 contextes d’objet 50-7
temporaire 16-9 numéros de port 30-5 transactions 50-9
applications distantes connexions distantes 17-8–17-9, contextes de périphérique 7-1,
accès non autorisé 17-7 18-33, 30-2–30-3 31-7, 36-1
base de données 17-1, 17-4– accès non autorisé 17-7 contraintes 15-11, 19-25–19-26
17-10, 18-32 changement 14-27 contrôles 5-3–5-4
limitation 14-8 envoi/réception création 19-25
d’informations 30-11 désactivation 24-22

Index I-9
importation 15-12 expert 48-2, 48-4 destruction 42-6
personnalisées 24-23 fichiers associés 48-25 édition 18-8, 18-24, 26-3
contraintes de données Voir gestion d’événements 48-9, grilles 12-14
contraintes 48-11 insertion
contrôle éditeur 6-7 informations de type 49-2 d’enregistrements 18-26
ContrôleBD, page de la palette informations de version 48-23 liste des 26-2
des composants 2-11, 12-13, licence 48-5, 48-8 pour modifier les
26-2 méthodes 48-9, 48-11 données 42-8–42-13
contrôles 2-7 options 48-5 pour scruter les données 42-2–
changement 31-3 orientés données 48-9 42-7
dessin 40-7, 40-9, 41-4 pages de propriétés 48-13, réponse aux changements 42-7
dessinés par le 48-14, 48-17 représentation des
propriétaire 6-13, 6-15 propriétés 48-3, 48-9, 48-10 champs 12-14, 26-9
boîtes à options 2-20 liaison 48-12 saisie de données 19-18, 26-12,
boîtes liste 2-19 publication 48-17 26-16-26-17
dessin 6-15, 6-17 recensement 45-6, 48-18 contrôles pages 2-22
dimensionnement 6-15 test 45-7, 48-19 contrôles personnalisés
événements 6-15 contrôles ActiveX,propriétés bibliothèques 31-5
styles 6-14 persistantes 48-15 contrôles préexistants 31-5
fenêtrés 31-4 contrôles animation 2-26, 7-30– contrôles texte 2-14
forme 40-7 7-32 contrôles de saisie 2-13
génération de contrôles exemple 7-31 contrôles texte formaté 2-13
ActiveX 48-3 contrôles d’édition 26-2, 26-10 contrôleurs Automation 44-10,
graphiques 31-4, 36-4, formats d’édition de texte 44-11, 46-1, 47-8
40-1-40-10 formaté 26-11 accès aux propriétés et
comparés aux bitmaps multiligne 26-10 méthodes 47-7
vs. 40-3 sélection de texte 6-9, 6-10 clients COM 44-9
création 31-4, 40-3 contrôles d’édition de texte connexion à un serveur 46-3
dessin 40-3–40-10 formaté 26-11 création à partir de
enregistrement des contrôles de saisie 2-13–2-14, bibliothèques de types 46-2
ressources système 31-4 2-15 exemple 46-5
événements 36-7 IDispatch, interface 47-8
contrôles de texte
options d’affichage 2-12 utilisation d’une interface de
multiligne 26-10, 26-11
palettes et 36-5–36-6 répartition 46-4
parcourir 2-12
contrôles éditeur de texte
formaté 2-14, 6-7 utilisation d’une interface
personnalisés 31-4 double 46-4
position 2-12 propriétés 2-14
contrôles en-têtes 2-22 controlling Unknown 3-24
pour modifier les
contrôles flèches haut-bas 2-16 ControlType, propriété 27-10,
données 42-8–42-13
pour scruter les contrôles liste 2-19–2-21 27-17
données 42-2-42-7 contrôles masque de saisie 2-13 conventions d’appel
réception de la contrôles mémo 2-13, 6-7 données membres 34-3
focalisation 31-4 propriétés 2-14 événements 34-9
redimensionnement 36-7, 41-4 contrôles onglets 2-22 méthodes 35-2
taille 2-12 contrôles orientés propriétés 33-7
données 12-13, 19-21, 26-1, types d’enregistrement de
contrôles ActiveX 44-10, 44-12,
42-1 message 37-6
44-19, 44-20
à partir de contrôles VCL 48-3 affichage des conversions 19-20, 19-22, 20-26
données 21-17, 26-5–26-6 chaîne 3-33
à partir de fiches VCL 48-7
dans les grilles 26-18, 26-20, PChar 3-33
applications Web 44-13
bibliothèques de types 44-14, 26-24, 26-31 coordonnées
48-3, 49-37 ensembles de données position de dessin
compression de fichiers multiples 26-35 actuelle 7-26
CAB 48-23 valeurs en cours 26-10 copie
création 48-1, 48-4–48-18 affichage des graphiques 26-12 ensembles de données 20-24
dates de validité 48-26 ajout 26-1–26-2 Copier, référentiel d’objets 2-42
déploiement Web 48-19 création 42-2–42-13 CopyFile, fonction 3-40
éléments 48-2 désactivation 18-14,26-5

I-10 Guide du développeur


CopyFrom, fonction 3-45 création DataSet, propriété 26-7
CopyMode, propriété 36-3 ensembles de données 24-29 founisseurs 15-1
CopyRect, méthode 7-4, 36-3, tables 13-11, 13-16 grilles de données 26-19
36-7 Créer le DataSet, DataSetCount, propriété 17-10,
CopyToClipboard, commande 13-17 23-9
méthode 6-10, 26-11 Créer un sous-menu, DataSetField, propriété
graphiques 26-12 commande (Menu ensembles de données
CORBA 28-22 Concepteur) 5-22, 5-25 client 24-4
applications de bases de critères de recherche 18-17, DataSets, propriété 17-10, 23-9
données multiniveaux 14-12 18-18 DataSource, composant
COM et 28-1 cryptage ajout 26-6, 26-7
connexion aux serveurs TSocketConnection 14-24 événements 26-8
d’applications 14-25 Ctrl3D, propriété 2-12 propriétés 26-6–26-8
initialisation 28-13 cubes de décision 27-8–27-9 DataSource, propriété 26-2,
normes 28-1 affichage des données 27-12 26-15, 26-35
présentation 28-5 dimensions paginées 27-23 contrôles orientés
variables dimensions, ouverture/ données 42-5, 42-6
d’environnement 28-18 fermeture 27-11 grilles de données 26-19
vue générale 28-2 obtention de valeurs 27-5, 27-6 requêtes 21-11
CORBA, applications 4-13 options de conception 27-10 date, champs 19-18
présentation 28-5 paramètres de formatage des valeurs 19-19
CORBA, threads et 8-13 dimensions 27-9, 27-22 date/heure, champs 19-18
CorbaBind, fonction 28-14 Currency, propriété 19-15 dates
correspondances entre curseur 18-10 internationalisation 10-9
claviers 10-10 déplacement 18-12, 20-7, 20-8, saisie 2-21
couleurs 26-8 DateTimePicker,
crayons 7-6 avec conditions 18-17 composant 2-21
internationalisation et 10-9 vers la dernière ligne 18-11, DAX 44-2
Count, propriété 2-35 18-14 Day, propriété 41-5
TSessionList 16-18 vers la première ligne 18-11, DB/2, pilote
courtage de connexions 14-26 18-13 déploiement 11-6
courtier d’objets 14-26 requêtes et 21-17 dBASE 13-12
courtier d’objets curseurs bidirectionnels 21-17 dBASE, tables 20-2, 20-3
d’entreprise 14-25 curseurs de glissement 6-2 accès aux données 20-5, 21-4
courtier de données 14-1 CurValue, propriété 19-25, ajout d’enregistrements 18-26
courtiers XML 14-38–14-40 25-31 création d’alias 16-12
crayons 7-5, 40-5 Custom, propriété 14-43 DatabaseName 13-4
changement 40-7 CustomConstraint, index 20-10
couleurs 7-6 propriété 19-15, 19-26 mémo, champs 26-10, 26-11
largeur 7-6 CutToClipboard, méthode 6-10, niveaux d’isolement 13-9
modes de dessin 7-30 26-11 ouverture des connexions 16-7
obtenir leur position 7-7 graphiques 26-12 parcours 20-6, 20-9
paramètres par défaut 7-6 requêtes 21-18
pinceaux 7-5 D transactions locales 13-10
position, définition 7-8, 7-26 DBChart, composant 12-15
style 7-7 Data, propriété DBCheckBox, composant 26-2,
Create, méthode ensembles de données 26-16
bases de données 17-3 client 24-15 DBCLIENT.DLL 24-1
constructeurs 2-10 Database, composants 17-1 DBComboBox, composant 26-2,
CreateDataSet, méthode 13-17 Database, propriété 18-32 26-13
CreateFile, fonction 3-40 DatabaseName, propriété 13-3, DBCtrlGrid, composant 26-2,
CreateSuspended, 17-5, 18-32, 18-33, 20-2 26-31–26-32
paramètre 8-11 DataChange, méthode 42-11 propriétés 26-32
CreateTable, méthode 20-19 DataField, propriété 26-13, DBEdit, composant 26-2, 26-10
CreateTransactionContextEx 26-15, 42-5, 42-6 DBGrid, composant 26-2, 26-18
exemple 50-21 DataSet, composant 18-2 événements 26-30
propriétés 26-24, 26-27

Index I - 11
DBGridColumns, statiques 32-8 DeleteSQL, propriété 25-13
composant 26-19 virtuelles 32-9 DeleteTable, méthode 20-18
DBHandle, propriété 18-32 nouveaux types de Delphi
DBImage, composant 26-2, composants 32-3 cadre de travail ActiveX
26-12 propriétés 33-3, 33-4–33-7, (DAX) 44-2
DBListBox, composant 26-2, 33-8, 33-12, 40-4 DELPHI32.DRO 2-40
26-13 stockées 33-12 Delta, propriété 24-24
types définis par dénomination
DBLocale, propriété 18-32
l’utilisateur 40-3 sessions de base de
DBLookupComboBox, variables
composant 26-2, 26-14–26-16 données 29-23
exemple 2-8
DBLookupListBox, $DENYPACKAGEUNIT,
déclarations de type
composant 26-2, 26-14–26-16 directive de compilation 9-12
objets 2-8
DBMemo, composant 26-2, propriétés 40-3
déplacements
26-10 fichiers 3-44
types énumérés 7-12
DBNavigator, composant 26-2, déclencheurs 12-7 déploiement
26-32–26-35 ActiveX, contrôles 11-3
DECnet 30-1 applications de base de
DBRadioGroup, décompte de références
composant 26-2, 26-17 données 11-4
COM interfaces 44-5 applications généralistes 11-1
DBRichEdit, composant 26-11 objets COM 44-4
DBSession, propriété 18-32 DLL, fichiers 11-4
déconnexion 16-8, 17-9 fontes 11-11
DBText, composant 26-2, 26-9 default MIDAS, applications 11-6
DCOM 44-7 directive 33-11, 39-3 moteur de bases de données
applications mot réservé 33-8 Borland 11-5
InternetExpress 14-37 Default, propriété 2-17 paquet, fichiers 11-3
applications éléments d’action 29-12 Web, applications 11-7
multiniveaux 14-10 DEFAULT_ORDER 24-7
connexion au serveur
déploiement des
DefaultColWidth, applications 11-1
d’applications 14-23 propriété 2-24
distribution d’applications 4-12 déploiement Web 48-19
DefaultDrawing, encodage 48-21
MTS 50-16 propriété 26-29, 26-31
DCOMCnfg.exe 14-37 options 48-22
DefaultExpression, DEPLOY.TXT 11-5, 11-12
.DCP, fichiers 9-2, 9-14 propriété 19-25
.DCR, fichiers 38-4 déréférencement de pointeurs
DefaultHandler, méthode 37-3 d’objet 32-10
.DCU, fichiers 9-2, 9-14 DefaultRowHeight,
débogage dérivation de classes 32-3, 32-9
propriété 2-24 dès que possible,
applications serveur définition
Web 29-27–29-31 désactivation 14-7
classes 32-3 désactivation des mises à jour
contrôles ActiveX 45-7, 48-19
définition de licences, contrôles en mémoire cache 25-3
Microsoft IIS, serveur 29-27
Netscape, serveurs 29-30
ActiveX 48-6 DescFields, propriété 13-17
objets MTS 50-22 définition des licences, pour DESIGNONLY, directive de
Personal Web, serveur 29-29 Internet Explorer 48-6 compilation 9-12
DDecision Cube, page de la définitions d’index dessin d’images 36-7
palette des composants 2-11, client, ensembles de dessin des contrôles 40-7, 40-9,
données 13-17 41-4
12-15
définitions des champs 20-20 destination, ensembles de
déclarations
classes 32-10, 40-5 définitions des index 20-20 données
protégées 32-6 délai, événements 8-11 définition 20-22
publiques 32-6 DELETE, instructions 21-14, Destroy, méthode 2-10
publiées 32-7 21-15, 25-13 destructeurs 2-10, 35-3, 42-6
gestionnaires Delete, méthode 18-8, 18-27 objets ayant un propriétaire
d’événements 34-6, 41-12 listes de chaînes 2-36, 2-37 et 40-5, 40-7
gestionnaires de DeleteAlias, méthode 16-12 détachement des bases de
messages 37-5, 37-7 DeleteFile, fonction 3-37 données 16-8, 17-9
méthodes 7-16, 35-4 DeleteFontResourc, détail, ensembles de
dynamiques 32-10 fonction 11-11 données 20-27–20-30
publiques 35-3

I-12 Guide du développeur


détails imbriqués 19-30–19-31, DisplayWidth, propriété 19-3, données en lecture seule
20-29 19-15, 26-19 ensembles de données
extraction à la demande 24-21 Dissocier attributs, client 24-5
lecture à la demande 15-3 commande 19-18 données membres de classe
DeviceType, propriété 7-33 distribuées déclaration 34-3, 40-4-40-5
.DFM, fichiers 10-10, 33-10 applications 4-10-4-13 double-clics
génération 10-13 DLL 28-13, 28-14 composants 38-13
Dialogues, page de la palette création 4-9 réponse aux 38-15–38-16
des composants 2-11 HTTP, serveurs 29-4, 29-5 Down, propriété 2-17
fichiers .DIB 26-12 incorporation dans le turboboutons 5-34
dictionnaire de données 12-5, document HTML 29-20 .DPK, fichiers 9-2, 9-8
19-17-19-18 internationalisation 10-12, .DPL, fichiers 9-2, 9-14
contraintes 15-12 10-13 DragCursor, propriété 2-13
différence majuscules/ paquets 9-1, 9-2 DragMode, propriété 2-13, 6-1
minuscules paramètres 28-15 grilles 26-29
comparaisons 18-22 serveurs COM 44-6 Draw, méthode 7-4, 36-3, 36-7
index 13-18, 24-8 .DMT, fichiers 5-27, 5-28 DrawShape 7-16
DII documents Active 44-10, 44-17 drintf, unité 12-6
référentiel d’interface 28-9 documents HTML DriverName, propriété 17-5
serveurs non Delphi 28-13 applications droits d’accès 20-5
DimensionMap, propriété 27-8 InternetExpress 14-35 DropConnections,
Dimensions, propriété 27-14 feuilles de style 14-42 méthode 16-9
directives modèles 14-40, 14-43–14-44 DropDownCount,
$H, compilation 3-27, 3-35 documents OLE propriété 2-20
$P, compilation 3-35 limites du marshaling 44-17 DropDownMenu,
$V, compilation 3-35 domaines ORB 28-20 propriété 5-38
$X, compilation 3-36 données DropDownRows,
default 33-11, 39-3 accès 42-1 propriété 26-16, 26-25
dynamic 32-10 affichage 19-21 dsBrowse, constante 18-7
override 32-9 dans les grilles 26-18, 26-20,
dsEdit, constante 18-8
protected 34-6 26-24, 26-31
désactivation de dsFilter, constante 18-10
public 34-6 DsgnIntf, unité 38-8
published 33-3, 34-6, 43-4 l’affichage 26-5
ensembles de données dsInsert, constante 18-8
stored 33-12
multiples 26-35 dsSetKey, constante 18-9
virtual 32-9
intervalles de durabilité
directives de compilation
rafraîchissement 26-5 dispenseurs de
chaînes 3-35
valeurs en cours 26-10 ressources 50-12
spécifiques au paquet 9-11
affichage seulement 26-9 transactions 50-8
DisableCommit 50-10
analyse 12-15 dynamic, directives 32-10
DisableConstraints, enregistrement 18-27
méthode 24-23
DisableControls, méthode 26-5
établissement de
rapports 12-18
E
DisabledImages, propriété 5-35 formats, E/S de fichier
Disconnect, méthode 46-3 internationalisation 10-9 types 3-40
Dispatch, méthode 37-3, 37-5 impression 12-18 EAbort 3-13
dispenseurs de ressources 50-6, modification 18-24, 18-28 écran
50-12 transactions et 13-10 rafraîchissement 7-2
dispID 44-13, 47-8 représentation graphique 12-15 écriture des paramètres de
liaison à 47-9 saisie de 18-25, 18-26 propriété 33-7, 38-9
dispinterfaces 14-28, 47-7 synchronisation 20-27 EDBEngineError, type 25-30
dispinterfaces, dans les sur plusieurs fiches 26-7, Edit, méthode 18-8, 18-24,
bibliothèques de types 49-14 26-8 38-10, 38-11
DisplayFormat, propriété 19-3, valeurs par défaut 19-25, 26-12 éditeur d’action
19-15, 19-19, 26-29 valeurs prédéfinies 26-13 ajout d’actions 29-9
DisplayLabel, propriété 19-15, données affichage changement d’actions 29-11
26-20 seulement 26-9

Index I - 13
éditeur de bibliothèques de éditeur de collection de EditRangeStart, méthode 20-17
types 49-2 paquets 9-15 éléments d’action 29-8, 29-9,
ajout d’interfaces 49-32 éditeur de colonnes 29-10–29-13
ajout de CoClasses 49-34 création de colonnes activation et
ajout de méthodes 49-33 persistantes 26-22 désactivation 29-11
ajout de propriétés 49-33 définition des listes de ajout 29-9
ajout de types énumérés 49-34 choix 26-23 chaînage 29-13
alias 49-19, 49-30 modification de l’ordre des générateurs de page et 29-21
attributs d’aide 49-7 colonnes 26-23 gestionnaires
barre d’état 49-6 suppression de colonnes 26-23 d’événements 29-8
barre d’outils 49-3 éditeur de cube de par défaut 29-10, 29-12
CoClasses 49-16, 49-29 décision 27-8 précaution de
création de bibliothèques de éditeur de masque de changement 29-7
types 49-23 saisie 19-18 sélection 29-11, 29-12
éléments principaux 49-3 éditeur de pages Web 14-41– éléments de menu 5-20
enregistrements 49-20, 49-30 14-42 ajout 5-20, 5-29
erreurs 49-6 éditeur de paramètres de définis 5-17
informations de type 49-7 procédure stockée 22-4 déplacement 5-23
interfaces 49-9, 49-27 activation 22-18 emplacements 5-25
interfaces CORBA 28-6 définition des groupe 5-21
interfaces de répartition 49-14, paramètres 22-15 imbrication 5-22
49-28 visualisation des initialiser les propriétés 5-24
modules 49-22, 49-31 paramètres 22-14 lettres soulignées 5-21
ouverture de éditeur de paramètres de lignes de séparation 5-21
bibliothèques 49-32 modification 5-24
requête 21-10
page Attributs 49-6 nommer 5-19, 5-29
éditeur de paramètres
page Indicateurs 49-7 sélection 6-11
page Texte 49-7
StoredProc 22-4
suppression 5-21, 5-25
page Utilise 49-8 éditeur de requête de
éléments Web
pages d’informations de décision 27-6
propriétés 14-42–14-43
type 49-6 démarrage 27-7
Ellipse, méthode 7-4, 7-11, 36-3
Pascal Objet ou IDL 49-24, éditeur des propriétés de base
ellipses
49-25 de données 17-5
dessin 7-11, 40-9
serveurs d’applications 14-19 affichage des paramètres de
connexion 17-6
Embed , balise HTML
syntaxe 49-7, 49-24, 49-25 (<EMBED>) 29-20
types énumérés 49-18, 49-29 éditeur Fichiers index 20-10
éditeurs de composants 38-13– empilement 6-4
unions 49-21, 49-30 emplacement des répertoires
vérification de la syntaxe 49-36 38-17
déploiement ActiveX 48-22
volet liste des objets 49-5 par défautt 38-13
recensement 38-16 EmptyStr, variable 3-32
éditeur de champs 2-40, 19-5 EmptyTable, méthode 20-18
application d’attributs de éditeurs de propriétés 2-27,
33-3, 38-7 EnableCommit 50-10
champ 19-17
attributs 38-11 EnableConstraints,
création de champs
persistants 19-6 boîtes de dialogue méthode 24-23
définition d’ensembles comme 38-10 EnableControls, méthode 26-5
d’attributs 19-17 comme classes dérivées 38-8 Enabled, propriété
modification de l’ordre des recensement 38-12–38-13 contrôles orientés
colonnes 26-29 EditFormat, propriété 19-3, données 26-4, 26-6
suppression d’ensembles 19-15, 19-19, 26-29 éléments d’action 29-11
d’attributs 19-18 édition de données 18-8 menus 5-29, 6-11
suppression d’objets édition de propriétés sources de données 26-7
champ 19-14 champ, objets 19-15 turboboutons 5-34
éditeur de code EditKey, méthode 20-6, 20-9 encapsulation 2-2
affichage 38-16 EditMask, propriété 19-15, encodage 48-21
gestionnaire d’événement 2-29 19-18 EndRead, méthode 8-8
ouverture des paquets 9-9 EditRangeEnd, méthode 20-17, EndWrite, méthode 8-8
20-18 enregistrement
graphiques 36-4

I-14 Guide du développeur


enregistrement actif 18-10 enregistrements en cours obtention de la valeur en
annulation des mises à jour en spécification 20-8 cours 25-31
mémoire cache 25-10 synchronisation 20-27 obtention des valeurs
spécification 20-8 Enregistrer attributs, précédentes 25-12, 25-31
synchronisation 20-27 commande 19-17 orientés BDE 18-31
Enregistrement de modèle, Enregistrer comme modèle, ouverture 18-3
boîte de dialogue 5-28 commande (menu sur serveurs distants 16-7
enregistrement en cours 18-10 Concepteur) 5-25, 5-28 parcours 18-7, 26-32
annulation des mises à jour en Enregistrer sous, personnalisés 12-18
mémoire cache 25-10 commande 13-17 recherche 18-9, 18-17–18-18
synchronisation 20-27 ensemble de données, référencement 25-28
enregistrements sources de données et 26-6
champs 12-16, 19-30–19-31
affichage 26-31 suppression
ensembles de données
intervalles de d’enregistrements 18-27
client 24-4
rafraîchissement 26-5 test de l’état 16-5
ensembles 33-2
ajout 18-8, 18-25–18-26, 18-28 ensembles de données
ensembles de données 12-15,
opérations groupées 20-21, ADO 12-16, 23-1, 23-28
18-2
20-24 accès aux données 13-14
accès aux 18-31, 26-7
comparés aux objets 2-2 caractéristiques
activation 17-10
déplacement dans les 18-23 communes 23-13
affichage de plusieurs 26-35
éditeur de bibliothèques de connecter 23-3
ajout d’enregistrements 18-8,
types 49-20, 49-30 connexion aux stockages de
18-25–18-26, 18-28
extraction 25-4 données 23-14
application des mises à jour en
filtrage 18-10, 18-19–18-22 déplacements 23-13
mémoire cache 25-6, 25-7
lecture seule 18-9 fichiers de données 23-18
association aux sessions 16-3,
marquage 18-15–18-16 lecture des données 23-21
17-4
mise à jour 18-29, 26-8 modification des
basés sur ADO 12-16–12-17
clients multiniveaux 24-24– données 23-13
composants d’aide à la
24-27 présentation 13-13, 23-20
décision 27-5–27-7
ensembles de données ensembles de données
multiples 25-6 copie 20-24
création 13-16 client 12-18, 14-3, 24-1–24-31
opérations groupées 20-24 ajout d’index 24-10
requêtes et 21-19 déplacement dans les 18-10–
18-14, 18-23 annulation des
mises à jour en mémoire cache modifications 24-6
et 25-4 édition 18-8
en tant que tables champs calculés 24-10
obtention de copie de données 24-14, 24-15
sous-ensemble 20-12 logiques 12-15
état par défaut 18-5 création de tables 24-29
parcours des 18-10–18-14, 26-8, données linéaires 24-28–24-31
26-32 états 18-4
modification 26-8 édition 24-5
rafraîchissement 24-27–24-28 enregistrement des
recherche 18-9, 18-17–18-18, fermeture 16-6, 17-9, 18-4, 18-6
sans déconnexion 17-9 modifications 24-6
20-5–20-9 fournisseurs et 24-16–24-28
portées spécifiques 20-13– sur serveurs distants 16-7
filtrage fusion des modifications 24-30
20-18 index 24-7–24-10
récupération 25-10–25-11 d’enregistrements 18-10,
18-19–18-22 ajout 24-7
régularisation des mises à lecture seule 24-5
jour 24-26 fournisseurs 15-2
gestion d’événements 18-29 limitation des
réitération de recherches 20-9 enregistrements 24-3, 24-19
suivi des actions 20-26 HTML, documents 29-26
imbriqués 12-16 mises à jour en mémoire
suppression 18-27, 20-18 cache 25-3
opérations groupées 20-25 Interbase direct 12-17–12-18
mise à jour 25-13, 25-24, 25-25, navigation 24-2
précaution 20-18 paramètres 24-17, 24-18
tri 20-10–20-12 25-28
modes 18-4 partage de données 24-16
avec des index permutation d’index 24-8
secondaires 20-10 modification 18-24, 26-8
modification des regroupement des
validation 26-5 données 24-9
grilles de données 26-28 données 18-24, 18-28
relations maître/détail 24-3

Index I - 15
requête de données 24-20 et commercial (&), événements, renommer par
spécification des caractère 5-21 erreur 49-33, 49-36
fournisseurs 24-17 événement, objets 8-10 évolutivité 12-8, 13-21
suppression d’index 24-8 événements 2-28, 26-6, 31-7, EWriteError 3-43
synthèse des données 24-11– 34-1–34-10 Exception 3-14
24-14 accès 34-6 exceptions 3-1–3-15, 35-2, 37-3,
ensembles de données de aide sur 38-4 43-5
décision 27-6 associer au classes 3-10
ensembles de données IB 12-17 gestionnairegestionnaires composant 3-12
ensembles de données multiples d’événements déclenchement 3-15
mise à jour 25-6 partagés 2-29 définies par l’utilisateur 3-14
ensembles de données attente d’ 8-10 ensembles de données 25-30
personnalisés 12-18 champ, objets 19-20 gestion 3-2
ensembles de résultat contrôles ActiveX 46-3, 46-6, imbriquées 3-3
lecture seule 25-25 48-9, 48-11 instances 3-9
mise à jour 21-19, 25-25 contrôles graphiques 36-7 redéclenchement 3-11
ensembles de résultats 21-14, courtiers XML 14-39 réponse aux 3-2
21-17–21-19 cubes de décision 27-8 RTL 3-6
curseurs et 21-17 définition de nouveaux 34-7– silencieuses 3-13
édition 21-18 34-10 exceptions, déclenchement dans
mise à jour 21-19 délai 8-11 les interfaces COM 49-10
obtention à l’exécution 21-14 ensembles de données 18-29 Exclusive, propriété 20-5
ensembles de résultats et objets 2-9 ExecProc, méthode 22-6, 23-25,
modifiables 21-18, 21-18–21-19 exposition à l’Automation 47-4 23-26
mise à jour 25-25 gestion des messages et 37-6 ExecSQL, méthode 21-14, 21-15
restrictions 21-18, 21-19 grilles de décision 27-14 objets mise à jour 25-23
en-têtes grilles de données 26-30 Execute, méthode 2-26, 8-4,
HTTP, requêtes 29-3 hérités 34-5
23-30, 43-5
en-têtes de colonne 26-20, 26-25 implémentation 34-2
TBatchMove 20-26
en-têtes de message niveau application 5-3
threads 30-13
nommer 34-9
(HTTP) 29-1, 29-3 ExecuteTarget, méthode 5-44
objets ASP 51-3
en-têtes de réponse 29-17 exécution de procédures
objets mise à jour 25-26–25-27
en-têtes de requête 29-14 objets MTS 50-17 stockées 22-5
enveloppes 31-5, 43-2 par défaut 2-28 exécution de requêtes 21-13–
initialisation 43-3 partagés 2-30 21-15
enveloppes de composant 31-5 récupérer 34-4 à partir de fichiers texte 21-8
environnement de traduction répondre aux 42-7 objets mise à jour 25-23
intégré 10-14 signalement 8-10 exemple "techniques de
EOF, marqueur 3-44 sources de décision 27-10 dessin" 7-24–7-30
EOF, propriété 18-12, 18-13 sources de données 26-8 exemples
EPasswordInvalid 3-15 souris applications service 4-6
EReadError 3-43 glisser-déplacer 6-1 Exemples, page de la palette
erreurs test 7-27 des composants 2-11
actions groupées 20-26 événements clavier 26-6, 34-4, Expanded, propriété 26-25
fournisseurs de données 15-10 34-10 TColumn 26-25
mises à jour en mémoire internationalisation 10-8 expert composant 31-9
cache 25-28–25-31 événements de clic 34-1-34-2 experts 2-40, 8-14
précaution 25-28 événements souris 7-24–7-27, composant 31-9
override, directive et 32-9 26-6, 40-2 CORBA, objet 28-5
sockets 30-9 définis 7-24 Module de données
erreurs de compilation glisser et déplacer 6-4 CORBA 14-17–14-18, 28-5
override, directive et 32-9 informations d’état 7-25 Module de données
erreurs de mise à jour paramètres 7-25 distant 14-15–14-16
messages de réponse 14-40 test 7-27 Module de données
erésolution 15-7, 15-10 événements standard 34-7 MTS 14-16–14-17
ErrorAddr, variable 3-15 événements timer 26-6 objet COM 45-1, 45-2

I-16 Guide du développeur


objet MTS 50-16 récupération des données fichiers .DFM 2-5
Ressource DLL 10-10 depuis 5-9–5-12 fichiers ADTG 23-18
explorateur de bases de référencement 5-2 fichiers d’application 11-2
données 4-10, 11-6 synchronisation des fichiers d’index 20-10
explorateur MTS 50-24 données 20-27 fichiers d’index non
explorateur SQL 14-3, 22-18 sur plusieurs 26-7, 26-8 productifs 20-10
Expose As CORBA Object, transmission d’arguments fichiers de contrôle réseau 16-14
commande 28-5 aux 5-8 .DLL, fichiers 11-4
expressions 19-25 utilisation des variables locales fichiers exécutables
expressions de valeur 19-25 pour créer 5-7 internationalisation 10-12,
extraction à la demande 24-22 variable globale pour 5-5 10-13
extraction fiches détail serveurs COM 44-6
mises à jour en mémoire cache fichiers INI 2-37
d’enregistrements 25-4
et 25-8
extraction incrémentale 24-21 fichiers linéaires 24-28–24-31
fiches HTML chargement 13-19, 24-29
HTML
F fiches 14-41
enregistrement 13-19, 24-30
tables imbriquées et 24-4
fabricants de classes 44-5, 44-6 fiches maître/détail 20-27 fichiers palette des
fabriques CORBA 28-8 mises à jour en mémoire cache
bitmaps 38-4
fenêtre et 25-8
fichiers ressources
classe 31-4 fiches modales 5-7 chargement 5-31
contrôles 31-4 fiches multiples 26-7, 26-8 et bibliothèques de types 44-15
gestion de message 41-4 fichier IDL 28-6 fichiers source
handles 31-4, 31-5 exportation à partir de la paquets 9-2, 9-8, 9-9, 9-14
fermeture bibliothèque de types 28-8
fichiers XML 23-18
ensembles de données 18-4 exportation depuis une
bibliothèque de types 49-37
FieldByName, méthode 19-24,
sur serveurs distants 16-7 20-15
fermeture des connexions 16-8, recensement 28-9
fichiers 3-36–3-45 FieldCount, propriété
17-9 champs persistants 26-21
FetchAll, méthode 18-34, 25-4 attributs 3-39
chaînes 3-43 FieldKind, propriété 19-15
FetchParams, méthode 24-18 FieldName, propriété 14-42,
copie 3-40
feuilles de style 14-42 19-8, 19-15
copie d’octets depuis 3-45
fiches champs persistants 26-21
déplacements 3-44
accès depuis d’autres grilles de décision 27-14
écriture vers 3-42
fiches 2-7 grilles de données 26-23, 26-25
envoi par le Web 29-18
affichage 5-6 Fields, propriété 19-23
graphiques 7-20–7-22, 36-4
ajout aux projets 2-6, 5-1 FileAge, fonction 3-40
handles 3-40, 3-41, 3-42
ajout de champs aux 7-27–7-28 FileExists, fonction 3-37
lecture depuis 3-42
ajout de références d’unité 5-2 FileGetDate, fonction 3-40
manipulation 3-36––3-45
comme types d’objets 2-3–2-5 FileName, propriété
modes 3-42
création lors de l’exécution 5-6 client, ensembles de
position 3-44
données affichage données 13-19
recherche 3-37
seulement 26-9 FileSetDate, fonction 3-40
renommer 3-39
en composants 43-1
ressource 5-31 FillRect, méthode 7-4, 36-3
gestion de la mémoire 5-5
routines Filter, propriété 18-19, 18-20
instanciation 2-3
bibliothèque Filtered, propriété 18-19
liaison 5-2 d’exécution 3-37
maître/détail, tables 12-14, filtres 18-10, 18-19–18-22, 20-12
date-heure 3-40 activation/désactivation 18-19
20-28–20-29 Windows API 3-41
modales 5-5 définition à l’exécution 18-22
suppression 3-37 ensembles de données
non modales 5-5, 5-7 taille 3-44
partage de gestionnaires client 24-3
types requêtes et 18-19
d’événements 7-15 E/S 3-40
perforation 12-14 requêtes ou 21-2
sans type 3-40 filtres de données 18-10, 18-19–
principale 5-1 texte 3-40
propriétés de requête 18-22, 20-12
typés 3-40 activation/désactivation 18-19
exemple 5-9 types incompatibles 3-41

Index I - 17
définition à l’exécution 18-22 lecture des propriétés 38-9, gestion d’événements 29-20,
requêtes et 18-19 38-11 29-21, 29-22
requêtes ou 21-2 nommer 35-2 générateurs de page 29-19–29-23
finally, mot réservé 36-7, 43-5 propriétés de lecture 33-7 chaînage 29-21
FindClose, procédure 3-37 Windows API 36-1 conversion de modèles 29-20
FindDatabase, méthode 16-9 Font, propriété 2-12, 7-4, 36-3 gestion d’événements 29-20,
FindFirst, fonction 3-37 en-têtes de colonne 26-25 29-21, 29-22
FindFirst, méthode 18-23 grilles de données 26-25 orientés données 29-24
FindKey, méthode 18-9, 20-6, mémon champs 26-11 générateurs de page ensemble
20-8 fontes 11-11 de données 29-24
EditKey ou 20-9 hauteur de 7-5 conversion de valeurs de
précaution d’utilisation 20-6 Footer, propriété 29-26 champ 29-24
FindLast, méthode 18-23 FOREIGN KEY, générateurs de table 29-24–
FindNearest, méthode 18-9, contrainte 15-12 29-26
20-6, 20-8 Format, propriété 27-14 définition de propriétés 29-25
précaution d’utilisation 20-6 formatage des données 19-17 générer le code de support
FindNext, fonction 3-37 applications d’événement 47-3
FindNext, méthode 18-23 internationales 10-9 gestion de la mémoire
FindPrior, méthode 18-23 formats personnalisés 19-20 interfaces 3-24
FindResourceHInstance, FormatCurr, fonction 19-19 objets COM 3-18
fonction 10-12 FormatDateTime, fonction 19-19 gestion des exceptions 3-1–3-15
FindSession, méthode 16-18 FormatFloat, fonction 19-19 blocs de protection de
First Impression 11-3 formats de données ressources 3-5
First, méthode 18-11 affectation 19-17 création d’un gestionnaire 3-7
FixedColor, propriété 2-24 personnalisation 19-20 déclaration de l’objet 3-14
formats monétaires 10-9 exécution du code de
FixedCols, propriété 2-24
formes 2-25, 7-11–7-12, 7-14 nettoyage 3-2
FixedOrder, propriété 2-19, 5-37
dessin 7-11, 7-14 flux de contrôle 3-3
FixedRows, propriété 2-24 gestionnaires par défaut 3-10
FixedSize, propriété 2-19 pourtour 7-5
remplissage 7-8 portée 3-9
FlipChildren, méthode 10-7 présentation 3-1–3-15
remplissage avec la propriété
FloodFill, méthode 7-4, 36-3 protection des allocations de
bitmap 7-9
flux 2-38 ressources 3-4
formes géométriques,
flux de fichier 3-41–3-45 protection des blocs de
dessin 40-9
création 3-41 code 3-2
E/S de fichier 3-41–3-45
Formula One 11-3
fournisseurs 14-3, 15-12 TApplication 3-13
exceptions 3-43 gestion mémoire
fin de marqueur 3-44 contraintes de données 15-11
ensembles de données client composants 2-10
mécanisme de flux de la gestionnaire de projet 5-2
VCL 3-41 et 24-16–24-28
gestion des erreurs 15-10 gestionnaire de propriétés
modification de la taille 3-44 partagées 50-13
ouverture 3-41 paquets de données 15-1
fournisseurs de données 14-19 gestionnaires d’événements 2-5,
portable 3-41
FoxPro 13-12 2-28–2-31, 19-20, 31-7, 42-7
TMemoryStream 3-41
FoxPro, tables 13-9, 20-5 affichage de l’éditeur de
flux de fichiers code 38-16
obtenir un handle 3-40 niveaux d’isolement 13-9
transactions locales 13-10 codage 2-28
flux de socket 30-13 coder 2-7
focalisation 19-21, 31-4 FrameRect, méthode 7-4
Free, méthode 2-10 contrôles dessinés par le
focalisation d’entrée 19-21 propriétaire 6-15
focalisation de saisie 31-4 FreeBookmark, méthode 18-16
déclarations 34-6, 41-12
FocusControl, méthode 19-21 définition 2-28
FocusControl, propriété 2-23 G dessin de lignes 7-27
fonctions 31-7 $G, directive de menus 2-30–2-31, 6-12
API Windows 31-4 compilation 9-12, 9-13 comme modèles 5-29
événements et 34-3 méthodes 34-3, 34-5, 34-6
GDI, applications 31-8, 36-1
graphiques 36-1 surcharge 34-6
générateurs de contenu 29-8,
paramètre Sender 2-30
29-18

I-18 Guide du développeur


paramètres 34-3, 34-8, 34-9 obtention d’informations lignes dessinées 7-5
notification d’état 6-4 changement de la largeur
d’événements 34-8 personnalisation 6-4 du crayon 7-6
partage 7-15 pointeur de la souris 6-4 listes de chaînes 6-15
partage du code parmi 7-15 glisser-empiler 2-13, 6-4–6-7 méthodes 36-3, 36-5, 36-7
partagés 2-31 Glyph, propriété 2-17, 5-33 copie d’images 36-7
pointeurs 34-3, 34-9 GotoBookmark, méthode 18-16 palettes 36-6
rechercher 2-29 GotoCurrent, méthode 20-27 outils de dessin 36-2, 36-8,
réponse aux clics de GotoKey, méthode 18-9, 20-7 40-5
bouton 7-13 précaution d’utilisation 20-6 changement 40-7
suppression 2-31 GotoNearest, méthode 18-9, présentation 36-1–36-3
transmission de paramètres présentation de la
20-7, 20-8
par référence 34-10 programmation 7-1–7-3
précaution d’utilisation 20-6
types 34-3 redessiner les images 36-7
Graph Custom Control 11-3
GetAliasDriverName, redimensionnement 7-21,
graphes de décision 27-15–27-20 26-12, 36-7
méthode 16-10 changement du type de
GetAliasNames, méthode 16-10 remplacement 7-21
graphe 27-19
GetAliasParams, méthode 16-10 stockage 36-4
comportements à
GetAttributes, méthode 38-11 suppression 7-23
l’exécution 27-21
GetBookmark, méthode 18-15 types d’objets 7-3
création 27-15
GetConfigParams, graphiques défilables 7-17
définition des séries de
méthode 16-10 données 27-19–27-20 graphiques indépendants des
GetData, méthode 19-21 états en cours des pivots 27-10 périphériques 36-1
GetDatabaseNames, modèles 27-18 graphiques, objets
méthode 16-10 options d’affichage 27-17 threads 8-5
GetDriverNames, personnalisation 27-17–27-19 GridLineWidth, propriété 2-24
méthode 16-10 présentation 27-15 grille de données 26-2
GetDriverParams, Graphic, propriété 7-19, 7-22, grilles 2-25, 26-2, 41-1, 41-2,
méthode 16-10 36-4 41-5, 41-11
GetFieldByName, graphiques 26-12, 36-1–36-8 affichage des données 26-18,
affichage 2-25–2-26 26-20, 26-24, 26-31
méthode 29-14
ajout au document ajout de lignes 18-25
GetFloatValue, méthode 38-9
HTML 29-19 dessin 26-29
GetIDsOfNames, méthode 47-8 édition 26-28
GetIndexNames, méthode 20-10 ajout de contrôles 7-18
associés à des chaînes 2-37 édition des données 26-4
GetMethodValue, méthode 38-9 état par défaut 26-19
GetOptionalParam, changement des images 7-21
chargement 7-20, 36-4, 36-5 restauration 26-25
méthode 15-5 gestion des événements 26-30
GetOrdValue, méthode 38-9 coller 7-23
complexes 36-6 insertion de colonnes 26-22
GetPalette, méthode 36-6 modification de l’ordre des
conteneurs 36-4
GetPassword, méthode 16-16 contrôles dessinés par le colonnes 26-23, 26-29
GetProcedureNames, propriétaire 6-13 obtention des valeurs 26-19,
méthode 23-11, 23-25 copie 7-22 26-20
GetProperties, méthode 38-11 dans les cadres 5-16 à l’exécution 26-21
GetSessionNames, dessin 7-4 options à l’exécution 26-27
méthode 16-18 dessin de lignes 7-10–7-11, orientées données 12-14, 26-31
GetStoredProcNames, 7-28–7-30 personnalisation 26-20–26-21
méthode 16-10 gestionnaires propriétés 26-32
GetStrValue, méthode 38-9 d’événements 7-27 saisie de données 26-23
GetTableNames, enregistrement 7-20 suppression de colonnes 26-21,
méthode 16-10, 23-10, 23-22 exemple "techniques de 26-23
GetValue, méthode 38-9 dessin" 7-24–7-30 suppression des
GetVersionEx, fonction 11-11 fichiers 7-20–7-22 colonnes 26-20
glisser-déplacer 2-13, 6-1–6-4 fonctions, appel 36-1 grilles de chaînes 2-25
DLL 6-4 formats de fichiers 7-3 grilles de couleurs 7-6
événements 40-2 indépendants 36-3 grilles de décision 27-12–27-14
internationalisation 10-9 affichage des données 27-13

Index I - 19
comportements à contexte de périphérique 7-2 générateurs de page 29-19–
l’exécution 27-21 sockets 30-6, 30-8 29-23
création 27-12 HandleException 3-13 générateurs de page ensemble
états en cours des pivots 27-10 HandleException, méthode 37-3 de données 29-24
événements 27-14 handles générateurs de table 29-24–
modification de l’ordre des connexions par socket 30-6, 29-26
colonnes/lignes 27-13 30-8 HTTP, messages de
présentation 27-12 modules ressource 10-12 réponse 29-4
propriétés 27-14 HandlesTarget, méthode 5-44 incorporation de tables 29-26
grilles de dessin 2-24 HasConstraints, propriété 19-15 modèles 29-19–29-20
grilles de données 12-14, 26-2, HasFormat, méthode 6-11, 7-23 HTML, tables 29-20, 29-26
26-31 Header, propriété 29-26 création 29-24–29-26
affichage des champs Height property définition des propriétés 29-25
ADT 26-25 TScreen 11-9 légendes 29-26
affichage des champs Height, propriété 2-12, 5-3, 26-13 HTMLDoc, propriété 14-40,
tableau 26-25 HelpContext, propriété 2-24 29-20
affichage des données 26-18, HelpFile, propriété 2-24 HTMLFile, propriété 29-20
26-20, 26-24, 26-31 héritage 2-2 HTTP 29-2
dessin 26-29 héritage de classe 2-6 applications de bases de
édition 26-28 données multiniveaux 14-11
hérite des classes 32-8
des données 26-4 code de statut 29-16
état par défaut 26-19
héritées
propriétés 40-2, 41-2 connexion au serveur
restauration 26-25 d’applications 14-24
gestion des événements 26-30 Hériter, référentiel d’objets 2-42
en-têtes de message 29-1
insertion de colonnes 26-22 heure
en-têtes de réponse 29-17
modification de l’ordre des internationalisation 10-9
en-têtes de requête 29-3, 29-14
colonnes 26-23, 26-29 saisie 2-21
et serveurs
obtention des valeurs 26-19, heure, champs 19-18 d’applications 14-15
26-20 formatage des valeurs 19-19
présentation 29-3–29-5
à l’exécution 26-21 HideSelection, propriété 2-14 HTTP, messages de réponse
options à l’exécution 26-27 hiérarchie (classes) 32-4 Voir messages de réponse
personnalisation 26-20–26-21 Hint, propriété 2-24 HTTP, messages de requête
présentation 26-18 Hints, propriété 26-35 Voir messages de requête
propriétés 26-32 HorzScrollBar 2-15 httpsrvr.dll 14-11, 14-24
saisie de données 26-23 Host, propriété
suppression de client, sockets 30-6
colonnes 26-20-26-21, 26-23 TSocketConnection 14-23 I
grilles tabulaires 26-31 HostName, propriété IAppServer, interface 14-5,
ggroupe de turboboutons 5-34 TCorbaConnection 14-25 14-8–14-9, 14-19–14-21, 14-31
Grouped, propriété hôtes 14-23, 30-4 IClassFactory, interface 44-5
boutons outils 5-36 adresses 30-4 icon
groupes de news 1-3 URL 29-2 objets graphiques 7-3
groupes de propriétés HotImages, propriété 5-35 icônes 2-25, 36-4
partagées 50-13 HotKey, propriété 2-16 barres d’outils 5-35
GroupIndex, propriété 2-17 HRESULT 47-10 vues arborescentes 2-20
menus 5-30 HTML IConnectPoint 47-3
turboboutons 5-34 applications IConnectPointContainer 47-3
GroupLayout, propriété 27-11 InternetExpress 14-35 ID référentiel 28-12
Groups, propriété 27-11 modèles par défaut 14-40, IDataIntercept, interface 14-24
GUID 3-20, 44-4 14-43 identificateurs
génération 3-20 HTML, commandes 29-19 données membres de
génération 29-20 classe 34-3
H informations de base de événements 34-9
données 29-24 incorrects 5-19
$H, directive de HTML, documents 29-3 interfaces de répartition 47-8
compilation 3-27, 3-35 bases de données et 29-23 liaison à 47-9
Handle, propriété 3-42, 31-4, ensembles de données 29-26 méthodes 35-2
31-5, 36-3

I-20 Guide du développeur


paramètres de propriété 33-7 ImeName, propriété 10-8 informations de version
types d’enregistrement de implémentation contrôles ActiveX 48-5, 48-8
message 37-6 d’événements 34-2 informatique nomade 13-20
idéogrammes 10-2 implements, mot clé 3-20, 3-21 Informix, pilotes
abréviations et 10-9 $IMPLICITBUILD, directive de déploiement 11-6
caractères larges et 10-3 compilation 9-11 Informix, serveurs 21-4
IDispatch, interface 44-8, 44-18, importation de données 20-21 INI (fichiers) 2-37
44-20, 47-8 ImportedConstraint, INI, fichiers
automation 44-12 propriété 19-15, 19-26 Win-CGI, programmes 29-6
identificateurs 47-8 $IMPORTEDDATA, directive initialisation
liaison à 47-9 de compilation 9-12 composants 33-12, 40-7
marshaling 44-18 Importer une bibliothèque de méthodes 33-12
suivi des membres 47-8 threads 8-3
types, commande 46-2
IDL (Interface Definition Indent, propriété 2-20, 5-34, Insérer depuis la ressource,
Language) 28-6, 44-14, 49-1 5-36, 5-37 commande (menu
IDL (Interface Description index 20-10–20-12, 33-9 Concepteur) 5-26, 5-31
Language) 44-16 actions groupées et 20-24, 20-25 Insérer depuis le modèle,
IDL, compilateur 44-16 client, ensembles de commande (menu
idl2ir 28-10 données 13-17 Concepteur) 5-25, 5-27
IETF, protocoles et ensembles de données Insert, commande (menu
standards 29-1 client 24-7–24-10 Concepteur) 5-25
IID 44-4 extraction 20-10, 20-11 INSERT, instructions 21-14,
Image, balise HTML recherche sur des clés 21-15, 25-13
(<IMG>) 29-19 partielles 20-8 Insert, méthode 18-8, 18-26
ImageIndex, propriété 5-35, regroupement des Append et 18-25
5-37, 5-42 données 24-9 listes de chaînes 2-36
ImageMap , balise HTML secondaires 20-9, 20-10 menus 5-29
(<MAP>) 29-20 tri sur des portées 20-14, 20-15, insertion
images 2-25, 7-18, 26-2, 36-3, 20-16 d’enregistrements 18-8, 18-26,
36-3–36-6 index primaires 20-10 20-24
affichage 2-25–2-26 actions groupées et 20-24, Insertion de modèle, boîte de
ajout 7-18 20-25 dialogue 5-27
ajout aux menus 5-23 index secondaires 20-9, 20-10 Insertion depuis la ressource,
boutons outils 5-35 recherche avec des 20-8 boîte de dialogue 5-31
changement 7-21 index, mot réservé 41-7 InsertObject, méthode 2-37
chargement 7-20 Index, propriété 19-15 InsertRecord, méthode 18-28
contrôles 7-17 IndexFieldCount, InsertSQL, propriété 25-13
contrôles pour 7-2 propriété 20-12 inspecteur d’objets 2-5, 2-26,
dans les cadres 5-16 IndexFieldNames, 33-2, 38-7
défilement 7-18 propriété 20-9, 20-11 aide sur 38-4
dessin 36-7, 40-8 IndexName ou 20-11 modification des propriétés de
effacement 7-23 IndexFields, propriété 20-12 tableau 33-3
enregistrement 7-20 IndexFiles, propriété 20-10 sélection des menus 5-26
internationalisation 10-9 IndexName, propriété 20-9, InstallShield Express 11-1
pinceaux 7-9 20-10, 20-11 déploiement de MIDAS 11-6
réaffichage 7-2 IndexFieldNames ou 20-11 déploiement de paquets 11-3
remplacement 7-21 IndexOf, méthode 2-35, 2-36 déploiement de SQL
renforcement du contrôle
indicateurs 42-4 Links 11-6
des 6-14
.INF, fichiers 48-21 déploiement des
images bitmaps
INFINITE, constante 8-11 applications 11-2
dans les cadres 5-16 déploiement du moteur de
information de type
Images, propriété bases de données
boutons outils 5-35
exécution 32-7
informations d’état Borland 11-5
IMalloc, interface 3-16 instanciation
communication 14-30–
IMarshal, interface 47-9 modules de données
14-32, 15-6
IME 10-8 événements souris 7-25 CORBA 14-18
ImeMode, propriété 10-8 informations de type 44-13

Index I - 21
modules de données destruction d’objets 3-22 optimisation 44-15
distants 14-16 DLL 28-15 paramètres 47-10
objets COM 45-2, 45-3 éditeur de bibliothèques de interfaces du composant
objets CORBA 28-5 types 49-9, 49-27 propriétés, déclaration 43-3
instructions d’affectation 33-2 éléments de programme non interfaces utilisateur
instructions SQL visuels 31-5 disposition 5-3–5-4
ADO 23-28 exécution 32-6 fiches 5-1
ensembles de données de extension du modèle InternalCalc, champs 19-8,
décision et 27-6 d’héritage simple 3-15, 3-16 24-10
fournies au client 24-20 gestion de la durée de internationales
IntegralHeight, propriété 2-19, vie 3-18, 3-22 applications 10-1
26-13 gestion de la mémoire 3-19, internationalisation 10-1
intégrité 3-22 Internet Engineering Task
violations 20-26 IID 3-20, 3-24 Force 29-1
intégrité des données 15-11 implémentation 28-7, 44-6 Internet Information Server
intégrité référentielle 12-7 internationalisation 10-8, 10-10, (IIS) 51-1
InterBase, pilote 10-13
Internet, page de la palette des
déploiement 11-6 interrogation dynamique 3-18
composants 2-11
InterBase, tables 21-4 IUknown,
implémentation 3-18
Internet, standards et
interface d’appel dynamique protocoles 29-1
liaison anticipée 14-28
Voir DII InternetExpress 14-32, 14-34–
liaison différée 28-4
interface de document 14-44
liaison tardive 14-28
multiple 4-1 liaisons dynamiques 3-19 InternetExpress, page palette de
interface de document marshaling 3-25 composants 14-35
unique 4-1 modules de données intervalles de
Interface Definition Language distants 14-19–14-21 rafraîchissement 26-5
(IDL) 28-6, 49-1 nom 28-5 intranets
Interface Description Language objets externes 3-21 noms d’hôte 30-4
(IDL) 44-16 objets internes 3-22 Voir aussi réseaux locaux
interface du composant opérateur as 3-19 InTransaction, propriété 13-7,
création 43-3 optimisation du code 3-23 23-12
interface, mot réservé 3-15 partage entre les classes 3-16 Invalidate, méthode 40-9
interfaces 3-15–3-25, 32-4, 32-6, polymorphisme 3-16 Invoke, méthode 47-8
43-2, 43-3 présentation 3-15–3-25 IObjectContext 50-7
agrégation 3-20, 3-21 procédures 3-17 IP, adresses 30-4, 30-6
appel dynamique 3-25 propriétés, déclaration 43-3 hôtes 30-4
applications distribuées 3-24 recensement 28-9 noms d’hôte 30-4
applications réutilisation du code 3-20 IPaint, interface 3-17
multiniveaux 14-8–14-9 squelettes et 28-3 IPersist, interface 3-16
appel 14-27 stubs et 28-2, 28-3 IProvideClassInfo, interface
Automation 47-7–47-9 TComponent 3-24 bibliothèques de types 44-14
bibliothèques de types et 44-12 utilisation 3-15–3-25 IProvider, interface
caractéristiques du interfaces COM, déclenchement création 12-12
langage 3-15 des exceptions 49-10 IProviderSupport, interface 15-2
CLSID 3-24 interfaces de répartition 47-8 IPX/SPX, protocoles 30-1
code exemple 3-16, 3-21, 3-23 attributs (éditeur de irep 28-9
COM 3-24, 44-1, 44-3, 45-3 bibliothèques de types) 49-14
composants 3-24
IRotate, interface 3-17
compatibilité des types 47-9 is, mot réservé 2-9
comptage de références 3-18, éditeur de bibliothèques de
3-19, 3-22–3-24 ISAPI 11-7
types 49-28
conception 32-7 ISAPI, applications 4-12, 29-5
identificateurs 47-8
CORBA 3-24, 28-3, 28-6–28-9 création 29-6
interfaces de types 44-15 débogage 29-27
types autorisés 28-7 interfaces doubles
Ctrl+Shift+G 3-20 messages de requête 29-7
compatibilité des types 47-9 IsCallerInRole, méthode 14-6
de répartition 47-8 marshaling automatique 44-18
délégation 3-20 isolation
MTS 50-17
dérivation 3-18 transactions 50-8

I-22 Guide du développeur


IsValidChar, méthode 19-21 KeyViolTableName, liens de données 20-28, 42-5–
ITE 10-14 propriété 20-27 42-7
ItemHeight, propriété 2-19 Kind, propriété 2-17 initialisation 42-6
boîtes à options 26-14 liens hypertextes
boîtes liste 26-13 L ajout au document
ItemIndex, propriété 2-19, 2-21 HTML 29-19
Items, propriété 2-19, 2-21 label 31-4 lignes 2-24, 18-25
boîtes à options 26-13 Last, méthode 18-11 dessin 7-10, 7-10–7-11, 7-28–
boîtes liste 26-13 Layout, propriété 2-17 7-30
contrôles radio 26-17 -LEpath, directive de dessin de gestionnaires
ITypeComp, interface 44-15 compilation 9-13 d’événements 7-27
ITypeInfo, interface 44-15 lecteurs de cassettes dessinées 7-5
ITypeLib, interface 44-15 audio-numériques 7-34 changement de la largeur
IUnknown, interface 3-18, 3-24, lecteurs média 7-32–7-35 du crayon 7-6
44-3, 44-4, 44-18, 44-19, 47-8 exemple 7-34 effacement 7-29
implémentée dans lecture des changements non grilles de décision 27-13
TInterfacedObject 3-19 validés 13-8 lignes de séparation
lecture des paramètres de (menus) 5-21
J propriété 38-9 limites
lecture incrémentale 14-31 portées de données 20-16
jeux de caractères 3-29, 10-2, lecture seule limites des rectangles 7-11
10-2–10-4 propriétés 32-6 Lines, propriété 2-14, 33-9
ANSI 10-2 lecture seule, champs 26-3 LineSize, propriété 2-16
conversions sur 2 octets 10-2 lecture seule, LineTo, méthode 7-4, 7-8, 7-10,
OEM 10-2 enregistrements 18-9 36-3
ordres de tri lecture seule, ensembles de Link, balise HTML (<A>) 29-19
internationaux 10-10 résultats 21-19 List, propriété 16-18
par défaut 10-2 liste Contient (paquets) 38-21
Left, propriété 2-12, 5-3
jeux de caractères sur deux LeftCol, propriété 2-24 liste Nécessite (paquets) 38-21
octets 10-2 Length, fonction 3-33 listes
jointures 21-16 liaison anticipée 14-28 chaînes 2-32–2-37
mises à jour en mémoire cache utilisation dans les threads 8-5
liaison avancée 28-13
et 25-26 listes à défilement 26-13
liaison de fiche 5-2
jointures hétérogènes 21-16 listes d’actions 2-29, 5-39–5-48
liaison de vtable
journal de modifications 24-5, listes de chaînes 2-32–2-37
liaison immédiate
24-30 COM 44-14 à court terme 2-33
annulation des à long terme 2-34
liaison différée 28-13
modifications 24-6 ajout d’objets 6-15
Automation 47-8
enregistrement des ajouter des chaînes 2-36
CORBA 28-4
modifications 24-6 contrôles dessinés par le
DII 28-4
juste à temps, activation 14-7 liaison dynamique 14-28, 28-13 propriétaire 6-15
CORBA 28-14 copier 2-37
K liaison statique 14-28, 28-13 créer 2-33
COM 44-14 déplacer 2-36
K, notes de bas de page dessinées par le
(système d’aide) 38-5 liaison tardive 14-28
propriétaire 6-13
KeepConnection, libellés 2-23, 10-9, 26-2
écrire dans un fichier 2-32
propriété 16-6, 17-8 colonnes 26-20
lire depuis un fichier 2-32
KeepConnections, libération des ressources 43-5
objets associés 2-37
propriété 16-6 libre, modèle de thread 45-5 parcourir 2-36
TSession, composant 13-4 licence position 2-35
KeyDown, méthode 42-10 contrôles ActiveX 48-5, 48-8 rechercher une chaîne 2-35
KeyExclusive, propriété 20-8, licence d’exécution 48-5, 48-8 supprimer des chaînes 2-36
20-16 licence de conception trier 2-36
ActiveForms 48-5, 48-8 listes de champs 19-5, 20-12
KeyField, propriété 26-16
contrôles ActiveX 48-5, 48-8 affichage 19-6, 19-7
KeyFieldCount, propriété 20-8
liens 20-28

Index I - 23
listes de champs -LUpackage, directive de mémoire dynamique 2-38
persistants 19-5 compilation 9-13 mémoires caches internes 25-1
affichage 19-6, 19-7 mémos 33-9
listes de choix 26-23 M menu items
listes de fichiers setting properties 5-25
déplacement des éléments 6-3 MainMenu, composant 5-17 Menu, propriété 5-30
faire glisser des maître/détail, fiches 12-14 menus 5-16–5-29
éléments 6-2, 6-3 maître/détail, relations 12-14 accès aux commandes 5-21
listes de recherche (système client, ensembles de affichage 5-24, 5-25
d’aide) 38-5 données 14-30 ajout 5-19–5-24
listes déroulantes 26-21 tables imbriquées 14-30 d’autres applications 5-31
affectation de valeurs 26-23 Mappings, propriété 20-25 d’images 5-23
ListField, propriété 26-16 Margin, propriété 2-17 déroulants 5-22–5-23
listres de chaînes marquage des déplacement parmi 5-26
dessinées par le enregistrements 18-15–18-16 enregistrer comme
propriétaire 6-14 marshaling 44-7 modèles 5-27, 5-28–5-29
ListSource, propriété 26-16 dans les interfaces gestion des événements 2-30–
-LNpath, directive de CORBA 28-3 2-31, 5-29
compilation 9-13 documents Active 44-17 internationalisation 10-9, 10-10
Loaded, méthode 33-12 documents OLE 44-17 modèles 5-19, 5-25, 5-27–5-29
données COM 47-9 chargement 5-27
LoadFromFile, méthode 21-8,
IDispatch, interface 44-12 suppression 5-28
23-18, 36-4 nommer 5-19
client, ensembles de interfaces COM 44-8
interfaces doubles 44-18 réutilisation 5-25
données 13-19
personnalisé 28-14 surgissants 6-12
ensembles de données
squelettes 28-8 menus contextuels
client 24-30
masques ajout d’éléments 38-14, 38-15
graphiques 7-20
édition de données 19-18 barres d’outils 5-38
listes de chaînes 2-32
MasterFields, propriété 20-27 concepteur de menus 5-25
LoadFromStream, méthode
MasterSource, propriété 20-27 menus déroulants 5-22–5-23
ensembles de données
Max, propriété 2-15 affichage 5-24
client 24-30
Max, propriété 2-24 menus déroulants et 5-22
localaddr, fichier 28-22
MaxDimensions, menus surgissants 6-12–6-13
locales 10-1
propriété 27-22 messages 5-4, 37-1–37-8, 41-4
formats des données et 10-9
MaxLength, propriété 2-14 clavier 42-9
modules ressource 10-10
contrôles d’édition de texte décomposeur 37-2
localisation 10-1, 10-13 définis 37-2
présentation 10-1 formaté 26-11
mémo, champs 26-10 par l’utilisateur 37-5, 37-7
localisation des enregistrements
ressources 10-10, 10-12-10-13 MaxRecords, propriété 14-38
types, déclaration 37-6
Locate, méthode 18-9, 18-17 MaxRows, propriété 29-25
gestion 37-5
Lock, méthode 8-7 MaxSummaries, propriété 27-22 gestionnaires 37-1, 37-3, 41-4
LockList, méthode 8-7 MaxValue, propriété 19-15 création 37-5–37-8
LoginPrompt, propriété 17-7, MBCS 3-30 déclarations 37-5, 37-7
23-8 MDI, applications par défaut 37-3
longues création 4-2 surcharge 37-4
chaînes 3-27 menus identificateurs 37-6
Lookup, méthode 18-9, 18-18 fusion 5-30–5-31 souris 42-9
LookupCache, propriété 19-12 mémo, champs 26-2, 26-10 messages d’erreur 25-30
LookupDataSet, texte formaté 26-11 internationalisation 10-10
propriété 19-12, 19-15 mémoire messages de la souris 42-9
LookupKeyFields, composants d’aide à la messages de réponse 29-8
décision 27-21 contenu 29-17, 29-18–29-26
propriété 19-12, 19-15
conservation 32-10 création 29-16–29-18, 29-18–
LookupResultField,
infiltrations dans les fiches 5-5 29-26
propriété 19-15 libération de bitmap 7-22
lParam, paramètre 37-2 envoi 29-13, 29-18
mémoire cache
LPK_TOOL.EXE 48-6 ressources 36-2

I-24 Guide du développeur


informations d’en-tête 29-16– répartition 32-8 régularisation des mises à
29-17 statiques 32-8 jour 24-25, 24-26
informations de base de suppression 2-31 requêtes et 21-19
données 29-23–29-26 surcharge 32-9, 37-3, 41-11 mise à jour, objets 25-13
informations de statut 29-16 virtuelles 32-9, 35-4 mise en correspondance des
réponse aux 29-17 MethodType, propriété 29-11, types de données 20-25
messages de requête 29-7, 29-8 29-15 mise en mémoire cache des
contenu 29-16 Microsoft threads 30-15
éléments d’action 29-10 ATL (Active Template mises à jour
HTTP, présentation 29-3–29-5 Library) 44-2 enregistrements multiple 15-4
informations d’en-tête 29-14– Microsoft IIS, serveur mises à jour en cascade 15-4
29-16 débogage 29-27 mises à jour en mémoire
répartition 29-9 Microsoft Server, DLL 29-5 cache 18-33, 18-34, 25-1
réponse aux 29-12–29-13 création 29-6 activation/désactivation 25-3
traitement 29-9 messages de requête 29-7 annulation 25-9–25-10
types 29-15 Microsoft SQL Server 13-12, application 25-5
messages du clavier 42-9 13-13 constantes de type
messages souris 37-2 Microsoft SQL Server, pilote d’enregistrement 25-11
métadonnées 20-24 déploiement 11-6 en suspens 25-4
obtention auprès des MIDAS 14-2, 14-3 ensembles de données client
fournisseurs 24-22 applications Web 14-32 et 25-3
métafichiers 2-25, 7-1, 7-17, base de données 14-32– extraction
7-20, 36-4 14-44 d’enregistrements 25-4
metafile construction 14-34–14-44 gestion des erreurs 25-28–
quand les utiliser 7-3 déploiement 11-6, 11-12 25-31
Method, propriété 29-15 fichiers 11-7 précaution 25-28
méthodes 7-15, 31-7, 35-1, 41-10 licences serveur 14-3 présentation 25-1–25-3, 25-26
appel 34-6, 35-3, 40-4 MIDAS (Multi-tier Distributed récupération
Automation et 47-7 Application Services d’enregistrements 25-10–
champ, objets 19-20 Suite) 14-2, 14-3 25-11
contrôles ActiveX 48-9, 48-11 MIDAS, page (palette des requêtes et 21-19
déclaration 7-16, 35-4 composants) 14-3, 14-22 support du moteur de bases
dynamiques 32-10 MIDAS, page de la palette des de données Borland 13-11
publiques 35-3 composants 2-11 vérification de l’état des 25-12
statique 32-8 MIDAS.DLL 11-6, 13-15, 14-3 mises à jour groupées 23-16–
virtuelles 32-9 MIDI, fichiers 7-34 23-18
dessin 40-8, 40-9 MKTYPLIB 44-16
MIDL
dynamiques 32-10 MM, film 7-34
génération des fichiers
et objets 2-2, 2-5 modales, fiches 5-5
d’en-tête 44-16
exposition à l’Automation 47-4 mode d’édition 18-24
génération du code proxy/
gestion des messages 37-1, annulation 18-25
stub 44-16
37-4 Voir aussi IDL Mode, propriété 20-23
gestionnaires crayons 7-5
MIME, messages 29-4
d’événements 34-3, 34-5, 34-6 modèle "briefcase" 13-20
surcharge 34-6
Min, propriété 2-15, 2-24
MinSize, propriété 2-16 modèle de thread
graphiques 36-3, 36-5, 36-7
MinValue, propriété 19-15 apartment 45-6
palettes 36-6
mise à jour d’enregistrements libre 45-5
héritées 34-6
initialisation 33-12 opérations groupées 20-24 modèle déconnecté 13-20
interruption 18-30 mise à jour des données 14-39– modèles 2-40, 2-42
nommer 35-2 14-40 composant 5-12, 5-13
pointeurs 34-9 mise à jour des graphes de décision 27-18
propriétés et 33-5–33-7, 35-1, enregistrements 14-39–14-40, menus 5-19, 5-25, 5-27–5-29
35-2, 40-4 26-8 chargement 5-27
protégées 35-3 clients multiniveaux 24-24– Web, applications 29-7
publiques 35-3 24-27 modèles de composants 5-12,
redéfinition 32-9 ensembles de données 5-13, 32-3
multiples 25-6 et cadres 5-14, 5-15, 5-16

Index I - 25
modèles de threads regroupement 14-8 MouseDown, méthode 42-9
contrôles ActiveX 48-5 sans état 14-7, 14-8, 14-30– MouseToCell, méthode 2-24
MTS 50-17 14-32 .MOV, fichiers 7-34
objets COM 45-2, 45-4 modules de données MTS Move, méthode
objets CORBA 28-6 attributs de transaction 14-17 listes de chaînes 2-36, 2-37
modèles HTML 14-43–14-44 expert 14-16-14-17 MoveBy, méthode 18-12
modèles threading 8-14 interface 14-20 MoveCount, propriété 20-26
modules de données modèles threading 14-17 MoveFile, fonction 3-40
CORBA 14-18 modules ressource 10-10, 10-12 MovePt 7-29
modules de données mois, renvoyer actuel 41-8 MoveTo, méthode 7-4, 7-8, 36-3
distants 14-15 monnaie .MPG, fichiers 7-34
modules de données internationalisation 10-9 MTS 4-12, 44-2, 44-9, 50-1
MTS 14-17 Month, propriété 41-5 activités 50-18
modèles, programmation 4-3 MonthCalendar, administration des objets 50-24
modes de configuration composant 2-21 applications de base de
sessions de base de moteur de bases de données données multiniveaux 14-6–
données 16-11 Borland 4-10, 12-2, 16-1, 18-32 14-8
modes de dessin 7-30 actions groupées 20-24, 20-25 callbacks 50-21
modification alias 16-11–16-12 clients de base 50-2, 50-15
d’enregistrements 20-24 connexions distantes 17-9 composants 50-2
données spécification 17-5, 17-6 connexion aux bases de
transactions et 13-10 API, appels 13-2 données 14-8
Modification de graphe, boîte appels directs 18-33 contextes d’objet 50-7
de dialogue 27-19 applications à niveau unique débogage d’applications
modification des propriétés et à niveau double 13-2– serveur Web 29-28–29-29
tableau 33-3 13-12 exigences 50-4
modifications, annulation 18-27 applications linéaires et 13-15 libération des ressources 50-6
Modified, méthode 42-12 applications Web et 11-7 paquets 50-14
Modified, propriété 2-14 connexion aux bases de pooling des connexions aux
Modifiers, propriété 2-16 données 13-5 bases de données 50-6
ModifyAlias, méthode 16-12 déploiement 11-4, 11-5, 11-12 pooling des objets 50-6
ModifySQL, propriété 25-13 détermination des types de proxy 14-21
modules 31-11 table 20-3 références aux objets 50-20
éditeur de bibliothèques de ensembles de données client regroupement des connexions
types 49-22, 49-31 et 12-18 de base de données 14-6
modules de données 2-38–2-40 extraction de données 18-31, sécurité 50-12,
applications Web et 29-7, 29-9 21-3, 21-15, 21-18 transactions 50-5, 50-7, 50-12
bases de données 17-11 fermeture des connexions de attributs 50-8
composants session et 16-19 base de données 16-8, 16-9 contrôlées par le client 50-10
créer 2-38 mise à jour des données 25-25, temporisations 50-11
distant ou standard 2-38 25-28 multimédia 7-35
distants Voir modules de noms de pilote 17-5 multiniveaux
données distants ouverture des connexions de architecture basée sur le
modifier 2-38 base de données 16-7 Web 14-32
règles de gestion 2-39 répertoires privés 16-15 architecture des
requêtes multitables 21-16 applications 14-4
modules de données CORBA
expert 14-17-14-18, 28-5 serveurs distants et 17-8, 17-9 Multi-niveaux, page (Nouveaux
instanciation 14-18 test des associations 16-9 éléments, boîte de
modèles threading 14-18 moteurs de bases de données, dialogue) 14-3
modules de données autres 11-5 MultiSelect, propriété 2-19
distants 14-3, 14-5, 14-13, motifs 7-9 multithread, applications 8-1
14-15–14-18 motifs de remplissage 7-8 multitraitement
expert 14-15-14-16 mots clés 38-5 threads 8-1
instanciation 14-16 protected 34-6 mutuellement exclusifs,
interfaces 14-19–14-21 mots de passe 17-2 options 5-34
modèles threading 14-15, 14-17 Paradox, tables 16-15
sessions et 13-4

I-26 Guide du développeur


N nouveau tri des champs 20-11
Nouveau, commande 31-11
non-visuels 2-9
personnalisation 2-6
Name, propriété 19-15, 26-7 Nouveaux éléments 2-42 propriétés 2-2
éléments de menu 2-30, 2-31 boîte de dialogue 2-40, 2-41 sans état 50-9
navigateur 18-11, 18-12, 26-2, Nouvel objet thread, boîte de temporaires 36-7
26-32–26-35 dialogue 8-2 objets adaptés aux threads 8-5
activation/désactivation des NSAPI, applications 4-12, 29-5 objets ADO
bouton 26-33 création 29-6 objet connexion 23-4
activation/désactivation des débogage 29-27 objet Field 23-1
boutons 26-34 messages de requête 29-7 objet Properties 23-1
boutons 26-33 null, valeurs 18-28 objets Automation 44-12
édition et 18-25 NumGlyphs, propriété 2-17 création 47-1
panneaux d’information 26-35 créer 51-2
suppression de données 18-27 O objets ayant un
navigateur de base de propriétaire 40-5–40-8
données 18-11, 18-12, 26-2, OAD 28-4, 28-9 initialisation 40-7
26-32–26-35 annulation du recensement objets COM 44-3, 44-5
activation/désactivation des d’objets 28-12 création 45-2
boutons 26-33, 26-34 exécution 28-10 définition 44-3
boutons 26-33 recensement des expert 45-2
édition et 18-25 serveurs 28-10–28-12 informations de type 49-2
panneaux d’information 26-35 oadutil 28-11 instanciation 45-3
suppression de données 18-27 Object , balise HTML mise à jour 44-6
navigation dans les ensembles (<OBJECT>) 29-20 modèles de threads 45-4
de données 18-10–18-14, 18-23 Object Activation Daemon recensement 45-6
NetBEUI, protocole 17-8 (OAD) 28-4, 28-9 test 45-7
NetFileDir, propriété 16-14 annulation du recensement vérification de type 44-13,
Netscape Server, DLL 29-5 d’objets 28-12 44-15
création 29-6 exécution 28-10 objets CORBA
messages de requête 29-7 recensement des affichage 28-17
Netscape, serveurs serveurs 28-10–28-12 définition des interfaces 28-6–
débogage 29-30 Object Management Group 28-9
NewValue, propriété 25-31 (OMG) 28-1 dissimulation 28-17
Next, méthode 18-12 Object Request Broker expert 28-5
niveaux 14-1 (ORB) 28-1, 28-16 instanciation 28-5
niveaux d’isolement 13-8–13-9 ObjectContext threads 28-6
ODBC, pilotes 13-9 exemple 50-22 objets distribués
nom de démarrage du ObjectName, propriété COM 4-12
service 4-8 TCorbaConnection 14-25 CORBA 4-13, 28-1
Objects, propriété 2-25 threads 8-13
nombres 33-2
formatage 19-19 listes de chaînes 2-37, 6-16 objets image 36-4
internationalisation 10-9 objet MTS, expert 50-16 objets mise à jour
valeurs de propriété 33-11 objet, champs 19-27 application 25-21
noms d’hôte 30-4 types 19-27 exécution d’instructions 25-23
IP, adresses ou 30-4 objets 2-2–2-10 gestion des événements 25-26–
25-27
noms de pilote 17-5 accès 2-7
création 2-9 préparation des instructions
non modales, fiches 5-5 SQL 25-15
NOT NULL UNIQUE, déclarations de type 2-8
définition 2-2 objets MTS 50-2
contrainte 15-12 administration 50-24
NOT NULL, contrainte 15-11 destruction 2-10
distribués 28-1 création 50-16
notes de dernière minute 11-12 débogage 50-22
notification d’événements 34-8 et événements 2-5
glisser-déplacer 6-1 débogage et test 50-22
Nouveau champ, boîte de exigences 50-4
dialogue 19-8 héritage 2-6
héritage de classes 2-7 installation 50-23
définition de champs 19-9, partage des propriétés 50-13
19-10, 19-11, 19-13 instances multiples 2-3
instanciation 2-3 objets orientés threads 8-5

Index I - 27
objets requête OnClientDisconnect, OnGetData, événement 15-6
informations d’en-tête 29-8 événement 30-8 OnGetDataSetProperties,
objets temporaires 36-7 OnClientRead, événement événement 15-5
objets thread serveur,sockets 30-11 OnGetSocket, événement
initialisation 8-3 OnClientWrite, événement serveur, sockets 30-10
objets utilitaires 2-31 serveur,sockets 30-11 OnGetTableName,
objets visuels OnColEnter, événement 26-30 événement 15-11
inter-processus 44-10 OnColExit, événement 26-30 OnGetText, événement 19-19,
.OCX, fichiers 11-3 OnColumnMoved, 19-20
ODBC 23-2 événement 26-29, 26-30, 26-31 OnGetThread, événement 30-10
ODBC, pilotes 17-8, 17-9 OnCommitTransComplete, onglets
ADO 13-13 événement 23-12 draw-item, événements 6-17
niveaux d’isolement 13-9 OnConnect, événement styles dessinés par le
utilisant ADO 13-13 client, sockets 30-9 propriétaire 6-14
ODL (Object Description OnConnectComplete, OnHTMLTag, événement 14-44,
Language) 44-14, 44-16, 49-1 événement 23-5 29-20, 29-21, 29-22
OEM, jeux de caractères 10-2 OnConnecting, événement OnKeyDown, événement 26-31,
OEMConvert, propriété 2-14 client, sockets 30-9 34-5, 42-10
OldValue, propriété 25-12, OnConstrainedResize, OnKeyPress, événement 26-31,
25-31 événement 5-4 34-5
OLE OnCreate, événement 31-13 OnKeyUp, événement 26-31,
applications OnDataChange, 34-5
fusion de menus 5-30 événement 26-8, 42-7, 42-11 OnLayoutChange,
connexion aux serveurs OnDataRequest, événement 27-10
d’applications 14-25 événement 15-11, 24-28 OnListen, événement
OLE 32.dll 44-2 OnDblClick, événement 26-30, serveur, sockets 30-10
OLE DB 13-13, 23-2 34-5 OnLogin, événement 17-3, 17-7,
OLEAut32.dll 44-2 OnDecisionDrawCell, 23-8
OLEnterprise 11-6, 14-12, 14-15 événement 27-14 OnLookup, événement
connexion aux serveurs OnDecisionExamineCell, client, sockets 30-9
d’applications 14-25 événement 27-14 OnMeasureItem,
OLEView 44-16 OnDisconnect, événement 23-5 événement 6-16
OMG 28-1 client, sockets 30-7 OnMouseDown,
OnAccept, événement OnDragDrop, événement 6-3, événement 7-25, 34-5, 42-9
serveur, sockets 30-10 26-30, 34-5 paramètres transmis à 7-25
OnAction, événement 29-13 OnDragOver, événement 6-2, OnMouseMove,
OnAfterPivot, événement 27-10 26-30, 34-5 événement 7-25, 7-26, 34-5
OnBeforePivot, OnDrawCell, événement 2-24 paramètres transmis à 7-25
événement 27-10 OnDrawColumnCell, OnMouseUp, événement 7-14,
OnBeginTransComplete, événement 26-29, 26-30 7-25, 7-26, 34-5
événement 23-12 OnDrawDataCell, paramètres transmis à 7-25
OnCalcFields, événement 18-10, événement 26-30 OnNewDimensions,
19-10 OnDrawItem, événement 6-17 événement 27-10
OnCalcFields, OnEditButtonClick, OnPaint, événement 2-25, 7-2
événements 18-30 événement 26-30 OnPassword, événement 16-16,
OnCellClick, événement 26-30 OnEndDrag, événement 6-3, 17-2
OnChange, événement 19-20, 26-30, 34-5 OnPopup, événement 6-12
36-7, 40-7, 41-12, 42-12 OnEndPage, événement 51-3 OnRead, événement
OnClick, événement 2-17, 34-1, OnEnter, événement 26-30, client, sockets 30-11
34-3, 34-5 26-34, 26-35, 34-5 OnReconcileError,
menus 2-30 OnError, événement événement 24-25, 24-26
OnClick,événement sockets 30-9 OnRefresh, événement 27-8
boutons 2-4 OnExit, événement 26-30 OnResize, événement 7-2
OnClientConnect, OnFilterRecord, OnRollbackTransComplete,
événement 30-7 événement 18-10, 18-19, 18-21 événement 23-12
serveur, sockets 30-10

I-28 Guide du développeur


OnScroll, événement 2-15
OnSetText, événement 19-19,
exécution 20-25
gestion des erreurs 20-26
P
19-20 mise à jour de données 20-24 $P, directive de
OnStartDrag, événement 26-31 mise en correspondance des compilation 3-35
OnStartPage, événement 51-3 types de données 20-25 page Encodage (déploiement
OnStartup, événement 16-5, mode d’importation 20-21 Web) 48-25
16-6 modes 20-23 page Fichiers supplémentaires
OnStateChange, suppression (déploiement Web) 48-24
événement 18-6, 26-8 d’enregistrements 20-25 page Paquets (déploiement
OnSummaryChange, optimisation des ressources Web) 48-24
événement 27-10 système 31-4 page Projet (déploiement
OnTerminate, événement 8-6 optimisation du code 7-15 Web) 48-22
OnThreadStart, événement interfaces 3-23 pages de code 10-2
serveur, sockets 30-10 Options de déploiement Web, pages de propriétés
OnTitleClick, événement 26-31 boîte de dialogue 48-20 actualisation 48-16
OnUpdateData, options de projet 4-2 actualisation des contrôles
événement 15-6, 15-7, 26-8 par défaut 4-3 ActiveX 48-16
OnUpdateError, options du compilateur 4-2 ajout de contrôles 48-15
événement 15-10, 18-34, 25-11, options du projet par défaut 4-2 association aux propriétés des
25-28 Options du projet, boîte de contrôles ActiveX 48-16
OnUpdateRecord, dialogue 4-2 contrôles ActiveX 48-13, 48-14,
événement 18-34, 25-28, 25-30 Options, propriété 2-24 48-17
mises à jour en mémoire grilles de décision 27-14 création 48-15
cache 25-26 grilles de données 26-27, 26-28 pages Web
objets mise à jour 25-13, 25-22, Options, propriétés producteur de page
25-23, 25-26 fournisseurs 15-3 MIDAS 14-40–14-44
OnValidate, événement 19-20 Oracle 13-13 PageSize, propriété 2-16
OnWillConnect, Oracle, pilotes Paint, méthode 36-7, 40-8, 40-9
événement 23-5 déploiement 11-6 palette des composants 2-11
OnWrite, événement Oracle8, tables 12-16 ajout de composants 9-7, 38-1,
client, sockets 30-11 limites à la création de 13-12 38-4
Open, méthode 23-5, 23-25, ORB 28-1, 28-16 ajout de serveurs COM 46-2
23-26 ORDER BY, clause 20-11 ajout de sessions de base de
bases de données 17-7 ordre de tri 10-10 données 16-17
ensembles de données 18-4 index 13-17, 24-7, 24-8 cadres 5-14
requêtes 21-14, 21-15 spécification 20-11 contrôles orientés
serveur, sockets 30-7 ordre de tri, définition 20-10 données 26-2
sessions 16-6 ordre de tri, spécification 20-11 création de bases de
tables 20-4 Orientation, propriété 2-15 données 17-3
grilles de données 26-32 pages 2-11
OpenDatabase, méthode 16-6,
Origin, propriété 7-29, 19-15 PaletteChanged, méthode 36-6
16-7
osagent 28-2, 28-3–28-4, 28-19 palettes 36-5–36-6
OpenSession, méthode 16-18
outils de dessin 36-2, 36-8, 40-5 comportement par défaut 36-6
OpenString 3-29 spécification 36-6
opérateurs affectation par défaut 5-34
changement 7-13, 40-7 PanelHeight, propriété 26-32
filtres de données 18-21
gestion de plusieurs outils Panels, propriété 2-23
opérateurs de chaîne 3-36
dans une application 7-12 PanelWidth, propriété 26-32
opérateurs de
test 7-12 panneaux d’informations 26-35
comparaison 18-21
ouverture PAnsiChar 3-28
opérateurs logiques 18-21
ensembles de données PAnsiString 3-33
opérations avec effet 36-7 sur serveurs distants 16-7 paquet, fichiers 11-3
opérations groupées 20-21 Overload, propriété 22-19
ajout de données 20-24
paquets 9-1–9-16, 38-21
override, directive 32-9 compilation 9-11–9-14
copie d’ensembles de
données 20-24 Owner, propriété 2-10, 31-13 options 9-11
composants 38-21
définition 20-22–20-23
conception 9-1, 9-6–9-7

Index I - 29
création 4-9, 9-8–9-13 spécification 33-11 partie publiée des classes 32-7
déploiement spécification des valeurs Pascal Objet
d’applications 9-3, 9-14 propriété par défaut 33-11 présentation 2-1
directives de compilation 9-11 Paradox, tables 16-2, 20-2, 20-3 PasswordChar, propriété 2-14
DLL 9-1, 9-2 accès aux données 20-5, 21-4 PasteFromClipboard,
édition 9-9 actions groupées 20-26, 20-27 méthode 6-10, 26-11
exécution 9-1, 9-3–9-5, 9-8 ajout d’enregistrements 18-26 graphiques 26-12
extensions de noms de création d’alias 16-12 PathInfo, propriété 29-11
fichiers 9-1 DatabaseName 13-4 pbmByName, constante 22-17
fichiers source 9-2, 9-14 droits d’accès pbmByNumber, constante 22-17
installation 9-6–9-7 insuffisants 16-16 PChar 3-28
internationalisation 10-12, extraction d’index 20-10 conversions chaîne 3-33
10-13 fichiers de contrôle
PDOXUSRS.NET 16-14
liste Contient 9-8, 9-11, 38-21 réseau 16-14
mémo, champs 26-10, 26-11
Pen, propriété 7-4, 7-5, 36-3
liste Nécessite 38-21
niveaux d’isolement 13-9 PenPos, propriété 7-4, 7-7
liste Requires 9-8, 9-10
modification 9-9 ouverture des connexions 16-7 PENWIN.DLL 9-13
options 9-9 parcours 20-6, 20-9 perforation, fiches 12-14
paramètres par défaut 9-9 protection par mot de performances, optimisation
personnalisés 9-5 passe 16-15 dans les bibliothèques de
référencement 9-4 répertoires 13-5 types 49-11
références dupliquées 9-10, requêtes 21-18 périphériques média 7-32
9-11 transactions locales 13-10 Personal Web, serveur
Seulement en conception, ParamBindMode, débogage 29-29
option 9-8 propriété 22-17 personnalisation de
utilisation 4-9 ParamByName, méthode 21-11, composants 33-1
utilisation dans des 23-19, 23-31 PickList, propriété 26-23, 26-25
applications 9-3–9-5 Parameters, propriété 23-19, picture, objets 7-3
paquets de données 14-19, 15-1 23-31 Picture, propriété 2-25, 7-18
actualiser les enregistrements paramètres dans les cadres 5-16
modifiés 15-4 applications Pie, méthode 7-4
champs 15-3 multiniveaux 24-18 pilotes de base de données 12-2
contenu 15-1 classes comme 32-10 pinceaux 7-8–7-9, 40-5
en lecture seule 15-4 DLL appels 28-15 bitmap, propriété 7-9
informations ensembles de données changement 40-7
d’application 24-14 client 24-17 couleurs 7-8
informations définies par événements souris 7-25 styles 7-8
l’application 15-5 gestionnaires pivots de décision 27-11
lecture 15-5 d’événements 34-3, 34-8, 34-9 comportement à
modification 15-6, 15-7 HTML, balises 29-19 l’exécution 27-20
propriétés des champs 15-4 interfaces doubles 47-10 propriétés 27-11
restreindre les modifications messages 37-2, 37-6 Pixel, propriété 7-4, 36-3
du client 15-4 paramètres de propriété 33-7 pixels
unicité des propriétés de tableau 33-9 lecture et définition 7-9
enregistrements 15-3 paramètres d’entrée 22-11 Pixels, propriété 7-5, 7-9
XML 14-32, 14-34, 14-38 paramètres de propriété pmCopy, constante 7-30
lecture 14-38–14-39 écriture 33-9 pmNotXor, constante 7-30
paquets delta 15-6 lecture 33-7, 33-9 pointeur de la souris
modification 15-6–15-8 paramètres de sortie 22-11 glisser-déplacer 6-4
XML 14-38, 14-39–14-40 paramètres facultatifs 24-14 pointeurs
paquets MTS 50-23 paramètres optionnels 15-5 Automation 47-8
par défaut ParamName, propriété 14-42 classe 32-10
classe ancêtre 32-4 Params, propriété 17-6, 17-7 méthode 34-3
gestionnaires courtiers XML 14-38 valeurs de propriété par
message 37-3 requêtes 21-11 défaut 33-11
valeurs 19-25, 26-12 Parent, propriété 31-14 pointeurs de méthode 34-2,
valeurs de propriété 33-8 34-3, 34-9
ParentShowHint, propriété 2-24
modification 39-2, 39-3

I-30 Guide du développeur


points de suspension (...) privilèges 20-5 chargement 33-12
boutons dans les grilles 26-24 ProblemCount, propriété 20-26 colonnes 26-19, 26-20, 26-24
points de terminaison 30-5 ProblemTableName, réinitialisation 26-25
Polygon, méthode 7-5, 7-12 propriété 20-26, 20-27 comme classes 33-3
polygones 7-12 ProcedureName, composants enveloppe 43-3
dessin 7-12 propriété 23-24, 23-25 contrôles ActiveX 48-9, 48-10,
polylignes 7-10 procédures 31-7, 34-3 48-15
dessin 7-10 nommer 35-2 contrôles éditeur de texte
PolyLine, méthode 7-5, 7-10 paramètres de propriété 38-12 formaté 2-14
polymorphisme 2-2 procédures stockées 12-7, 12-16 cubes de décision 27-8
pooling des objets 50-6 ajout 22-4 déclaration 33-3, 33-4–33-7,
pooling des ressources 50-6 basées sur ADO 12-17 33-8, 33-12, 40-4
création 22-5 stockées 33-12
PopupMenu, composant 5-17
exécution 22-5 types définis par
PopupMenu, propriété 6-12 l’utilisateur 40-3
Port, propriété Interbase 12-17
en lecture seule 42-3
client, sockets 30-6 paramètres 22-11
ensembles de données orientés
serveur, sockets 30-7 performances 22-2
BDE 18-32
TSocketConnection 14-23 préparation 22-5
et objets 2-2
portée (objets) 2-7 surchargées 22-19
événements et 34-1, 34-2
portées 20-12 procédures stockées ADO 12-17
exposition à l’Automation 47-3
application 20-16 exécuter 23-26
grilles 26-32
filtres ou 20-12 présentation 23-24
de décision 27-14
limites 20-16 procédures stockées IB 12-17 héritées 33-3, 40-2, 41-2
modification 20-17 processus lents HTML, tables 29-25
spécification 20-14 utilisation de threads 8-1 initialisation 2-26
ports 30-5 processus parallèles lecture seule 32-7, 33-7
client, sockets 30-6 threads 8-1 mise à jour 31-7
connexions multiples 30-5 producteurs de page modification 38-7, 39-2, 39-3
domaines ORB 28-20 orientés données 14-40–14-44 du texte 38-9
serveur, sockets 30-7 profondeurs de couleur 11-8 nodefault 33-8
services et 30-2 profondeurs de couleur, partage entre objets MTS 50-13
Position, propriété 2-15, 2-24 programmation des 11-10 pivots de décision 27-11
Post, méthode 18-7, 18-8 programmation orientée présentation 31-6
Edit et 18-25 objet 2-2–2-10, 32-1–32-10 publication 41-2
pourtours applications distribuées 28-1, read et write 33-5
dessiner 7-5 28-2 redéclaration 33-11, 34-6
Precision, propriété 19-16 déclarations 32-3, 32-10 sources de décision 27-10
Prepare, méthode 21-8, 21-15, classes 32-6, 32-7 sources de données 26-6–26-8
22-5 méthodes 32-8, 32-9, 32-10 spécification des valeurs 33-11,
Presse-papiers 6-9, 26-11 définition 2-2 38-9
effacement de la sélection 6-11 héritage stockage 33-11
formats héritage 2-6 de données interne 33-4,
ajout 38-13, 38-16 programmes d’installation 11-2 33-7
graphiques et 7-22–7-24 projets stockage et chargement des
objets graphiques 26-12 ajout de fiches 5-1 propriétés non
test du contenu 6-11 modèles 2-42 publiées 33-13–33-14
PRIMARY KEY, propriétés 33-1–33-12 tableau 33-3, 33-9
contrainte 15-12 accès 33-5–33-7 types 33-2, 33-9, 38-9, 40-3
principales 28-17 aide sur 38-4 valeurs par défaut 33-8, 33-11
Prior, méthode 18-12 Automation 47-7 redéfinition 39-2, 39-3
priorités boîtes à options de visualisation 38-9
utilisation de threads 8-1 référence 26-15 propriétés du parent 2-12
utilisation des threads 8-3 boîtes de dialogue propriétés en écriture seule 33-7
Priority, priorité 8-3 standard 43-2 propriétés en lecture seule 32-6,
private 2-8 boîtes liste de référence 26-15 33-7, 42-3
PrivateDir, propriété 16-14 champ, objets 19-3, 19-14– propriétés héritées
19-19 publication 33-3

Index I - 31
propriétés initialisation 2-27 limitation 14-11 redimensionnement des 11-9,
propriétés privées 33-5 rapports 12-18 41-4 contrôles
protected 2-8 .RC, fichiers 5-31 graphiques 36-7
directive 34-6 Read, méthode références
événements 34-6 TFileStream 3-43 bibliothèques de types 49-8
mot clé 33-3, 34-6 read, méthode 33-7 fiches 5-2
protégée read, mot réservé 33-9, 40-4 paquets 9-4
partie des classes 32-6 ReadBuffer, méthode références circulaires 5-2
protocole (Digital) 30-1 TFileStream 3-43 références croisées 27-2–27-3,
protocoles README.TXT 11-12 27-12
choix 14-9–14-12 ReadOnly, propriété 2-14, 42-3, à plusieurs dimensions 27-3
connexion, composants 14-22 42-10 à une dimension 27-3
connexions réseau 17-8 contrôles d’édition de texte définition 27-2
Internet 29-1, 30-1 formaté 26-11 valeurs récapitulatives 27-3
ProviderFlags, propriété 15-9 contrôles orientés référentiel 5-12
ProviderName, propriété 14-21, données 19-16, 26-3 d’implémentation 28-4
14-38 grilles de données 26-25, 26-28 référentiel d’interfaces 28-4
proxy 44-7 mémo, champs 26-10 ajout d’interfaces 28-10
proxy MTS 14-21, 50-4 tables 20-5 exécution 28-9
PString 3-33 réalisation de palettes 36-5, 36-6 recensement des interfaces
public 2-8 ReasonString, propriété 29-17 CORBA 28-9, 28-9–28-10
directive 34-6 recensement suppression d’entrées 28-10
mot clé 34-6 composants 31-12 référentiel d’objets 2-40–2-43,
propriétés 33-10 contrôles ActiveX 48-18 5-12
publication éditeurs de composants 38-16 ajouter des éléments 2-41
propriétés éditeurs de propriétés 38-12– conversion d’applications
exemple 40-2, 41-2 38-13 serveur Web 29-31
publique interfaces CORBA 28-9 répertoire partagé 2-41
partie de classes 32-6 objets ASP 51-4 utiliser des éléments 2-41–2-42
published 2-8, 33-3 recherche Refresh 5-45
directive 33-3, 34-6, 43-4 d’enregistrements 18-9 Refresh, méthode 26-5
mot clé 34-6 réitération de recherches 20-9 RefreshLookupList,
propriétés 33-10, 33-11 recherche de données 18-9 propriété 19-13
PWideChar 3-28 clés partielles et 20-8 RefreshRecord, méthode 24-27
PWideString 3-33 recherches incrémentales 26-16 Register, méthode 7-3
réitération de recherches 20-9 Register, procédure 31-12, 38-2
Q recherche incrémentales 26-16
recherches indexées 18-9, 18-17,
RegisterComponents,
procédure 9-7, 31-12, 38-2
QReport, page de la palette des 18-18, 20-6 RegisterPooled, procédure 14-8
composants 2-11 RecNo, propriété RegisterPropertyEditor,
qualificateurs 2-7 ensembles de données procédure 38-12
Query, propriété client 24-2 RegisterTypeLib, fonction
objets mise à jour 25-19 RecordCount, propriété 20-26 bibliothèques de types 44-15
QueryInterface, méthode 3-18, RecordSet, propriété 23-15 registres 2-37, 10-9
3-22, 3-24 Recordset, propriété 23-31 règles d’entreprise 14-2
IUnknown 44-4 RecordSetState, propriété 23-15 règles de gestion
Rectangle, méthode 7-5, 7-11, modules de données 2-39
R 36-3 regroupement de
rectangles composants 2-22
raccourcis 2-12, 2-16 dessin 7-11, 40-9 regroupement de
ajout aux menus 5-21 rectangles à coins arrondis 7-11 contrôles 2-21–2-22
raccourcis clavier récupération d’enregistrements
ajout aux menus 5-21
regroupement des objets
en mémoire cache 25-10–25-11 modules de données
raise, mot réservé 3-15 redéclaration des distants 14-8
rappels propriétés 34-6 REGSERV32.EXE 11-3, 11-6
applications
redéfinition des méthodes 32-9
multiniveaux 14-20

I-32 Guide du développeur


régularisation des filtrage et 18-19 Ressource DLL
données 24-25 hétérogènes 21-16 basculement dynamique 10-13
relations maître/détail HTML tables 29-26 expert 10-10
ensembles de données Interbase 12-17 ressource, fichiers 5-31
client 24-3 mises à jour en mémoire cache ressources 31-8, 36-1
intégrité référentielle 12-7 et 25-26 chaînes 10-10
multiniveaux, multitables 21-16 isolement 10-10
applications 24-4 objets mise à jour et 25-13, libération 43-5
tables imbriquées 24-3 25-15 localisation 10-10, 10-12, 10-13
Release, méthode 3-18, 3-22, optimisation 21-13, 21-17 système, optimisation 31-4
3-24 préparation 21-15 ressources en mémoire
IUnknown 44-4 présentation 21-1–21-5 cache 36-2
TCriticalSection 8-7 sessions nommées et 16-3 ressources système,
Remote DataBroker 11-6 soumission conservation 31-4
RemoteServer, propriété 14-21, d’instructions 21-15 restauration d’enregistrements
14-38 substitution des supprimés 25-10
RemovePassword, paramètres 25-17, 25-22 RestoreDefaults, méthode 26-21
méthode 16-16 Web, applications 29-26
Result, paquet de
RenameFile, fonction 3-39, 3-40 requêtes ADO 12-17 données 24-24
présentation 23-22
répartiteur Web Result, paramètre 37-6
SQL 13-15, 23-23
objets à répartition résultat, paramètre 22-11
utilisation 23-22
automatique 29-10 Resume, méthode 8-11, 8-12
objets auto-répartis 14-39 requêtes de décision
obtention de valeurs 27-6 retour à la ligne 6-8
répertoires retour automatique 6-8
propriétés 27-7
fichiers temporaires 16-15 ReturnValue, propriété 8-9
répertoires privés 16-15 requêtes hétérogènes 21-16
requêtes IB 12-17 réutilisation du code
RepositoryID, propriété 14-25 technique 5-12
Request for Comment (RFC), requêtes multitables 21-16
requêtes paramétrées 21-6 RevertRecord, méthode 18-34,
documents 29-1 25-10, 25-11
création 21-9–21-13
RequestLive, propriété 21-18, à l’exécution 21-11 RFC, documents 29-1
25-25 définition 21-2 rôles
requête, composants 12-16, 21-1 exécution à partir de fichiers sécurité en fonction des
ajout 21-4 texte 21-9 rôles 50-12
requête, partie (URL) 29-2 requêtes SQL Rollback, méthode 13-8
requêtes 12-16, 18-4, 21-1 ensembles de résultat RollbackTrans, méthode 23-12
basées sur ADO 12-17 mise à jour 25-25 RoundRect, méthode 7-5, 7-11
caractères d’espacement exécution 25-23 RowAttributes, propriété 29-25
et 21-6 objets mise à jour et 25-13, RowCount, propriété 26-16,
caractères spéciaux et 21-6 25-15 26-32
création 21-4, 21-7 substitution des RowHeights, propriété 2-24,
à l’exécution 21-7 paramètres 25-17, 25-22 6-16
définition de paramètres Requires, liste (paquets) 9-8, Rows, propriété 2-25
à l’exécution 21-11
9-10 RPC 44-8
définition des
réseaux rtDeleted, constante 25-11
instructions 21-6–21-9
accès aux données 25-2 rtInserted, constante 25-11
définition des
connexion aux 17-8 rtModified, constante 25-11
paramètres 21-9–21-13
couche de communication 28-2 RTTI 32-7
ensembles de résultat
mise à jour 21-19, 25-25
réseaux locaux 28-3 rtUnmodified, constante 25-11
ensembles de résultats 21-14, connexion 28-21–28-22 $RUNONLY, directive de
21-17–21-19 ResetEvent, méthode 8-10 compilation 9-12
curseurs et 21-17 résolution 15-1, 15-6
mise à jour 21-19 résolution d’écran 11-8 S
obtention à l’exécution 21-14 résolution d’écran,
exécution 21-13–21-15, 25-23 programmation de la 11-9 SafeArray 49-25
à partir de fichiers resourcestring, mot safecall, convention
texte 21-8 réservé 10-10 d’appel 48-10, 48-11, 49-10

Index I - 33
SafeRef 50-20 SelEnd, propriété 2-15 modules de données
sans état Self, paramètre 31-13 distants 2-40
objets 50-9 SelLength, propriété 2-14, 6-9 multiniveaux
SaveConfigFile, méthode 16-11 SelStart, propriété 2-15, 6-9 fournisseurs de
SaveToFile, méthode 7-20, SelStart, propriété données 14-19, 15-1
23-18, 36-4 2-14 threads 8-12
client, ensembles de SelText, propriété 2-14, 6-9 serveurs de bases de
données 13-19 Sender, paramètre 2-30 données 4-10, 17-7, 21-3
ensembles de données exemple 7-7 serveurs de bases de données
client 24-31 séparateurs distants Voir serveurs distants
listes de chaînes 2-32 composants 2-16 serveurs de messagerie
SaveToStream, méthode server, applications threads 8-12
ensembles de données extraction de données 21-3 Voir aussi applications serveur
client 24-31 ServerType, propriété 30-10, Web
ScaleBy, propriété 30-11, 30-12 serveurs distants 12-3, 16-7,
TCustomForm 11-9 serveur Automation 21-4, 44-7
Scaled, propriété connexion à un 46-3 accès aux données 25-1
TCustomForm 11-9 serveur, applications accès non autorisé 17-7
ScanLine, propriété extraction de données 21-16, attachement 17-8–17-9
bitmap 7-9 21-19 documents Active et 44-17
bitmap, exemple 7-19 interfaces 30-2 noms 14-23
ScktSrvr.exe 14-10, 14-14, 14-23 services 30-1 serveurs en processus 44-6
SCM 4-4 transactions 13-9 ActiveX 44-12
Screen, variable 5-3, 10-8 serveur, connexions 30-2, 30-3 ASP 51-3
scripts (URL) 29-2 numéros de port 30-5 serveurs hors processus 44-7
scripts de connexion 17-7 serveur, sockets 30-7–30-8 ASP 51-4
ScrollBars, propriété 2-24, 6-8 acceptation de clients 30-10 service
mémo, champs 26-11 acceptation de requêtes threads 4-6
SDK Microsoft Data client 30-7 Service Control Manager 4-4
Access 23-5 gestion d’événements 30-10 service d’annuaire 28-3
sections critiques 8-7 messages d’erreur 30-8 Service, propriété
précaution d’utilisation 8-8 socket Windows, objets 30-7, client, sockets 30-6
Sections, propriété 2-22 30-8 serveur, sockets 30-7
securité 17-7 spécification 30-6 services 4-3–4-8
applications multiniveaux 14-2 utilisation de threads 30-12 annuaire 28-2, 28-3–28-4
bases de données 12-4 serveurs ActiveX code exemple 4-4, 4-6
connexions Web 14-11, 14-24 bibliothèques de types 44-13 CORBA 28-2
dBase, tables 13-4 optimisation 44-15 demande de 30-6
DCOM 14-37 vérification de type 44-15 désinstallation 4-4
évolutivité et 12-9 serveurs Automation 44-10, implémentation 30-1–30-2, 30-7
MTS 14-6, 14-10, 50-12 44-11, 44-19 installation 4-4
Paradox, tables 13-4 accès aux objets 47-8 ports et 30-2
recensement pour les création 47-1, 47-9 propriétés de nom 4-8
connexions socket 14-10 test 47-6 réseau, serveurs 30-1
segments de ligne tester 51-5 services de support 1-3
connectés 7-10 serveurs COM 44-3, 44-5, 44-19 Session, composant 16-1, 16-2
SELECT, instructions 21-14, distants 44-7 Session, propriété 17-4
21-18 en processus 44-6 SessionName, propriété 16-4,
SelectAll, méthode 2-14 hors processus 44-7 17-4, 18-32, 18-33, 29-23
SelectCel,l méthode 42-4 serveurs COM et CORBA sessions 13-4, 16-1, 16-2, 17-10
SelectCell, méthode 41-13 combinés 28-5 activation 16-5, 16-6
Sélection de menu, boîte de serveurs d’applications 14-1, création 16-3–16-4, 16-18, 16-19
dialogue 5-26 14-13–14-21 décompte 16-18
contraintes de données 15-11 dénomination 16-4, 29-23
Selection, propriété 2-24
fournisseurs de données 15-1 désactivation des
Sélectionner un menu, connexions 16-6
commande (menu identification 14-22
état en cours 16-5
Concepteur) 5-25, 5-26

I-34 Guide du développeur


gestion des alias 13-4 démarrage 28-19 DML 23-22, 23-29
modes de configuration 16-11 localisation 28-3 utilisation avec ADO 13-15
multiples 16-2, 16-3, 16-17 socket Windows, objets 30-5 SQL direct 13-6, 13-9, 21-19
noms d’alias et 16-11 client, sockets 30-6 SQL Links 11-4, 13-5
obtention d’informations 16-10 clients 30-6 déploiement 11-5, 11-12
par défaut 16-2 serveur, sockets 30-7, 30-8 fichiers de pilote 11-6
redémarrage 16-6 socket, composants 30-5–30-8 pilotes 17-8
test des associations 16-13 socket, connexions installation 13-9
Web, applications 29-23 ouverture 30-6 SQL local 21-4, 21-16
Sessions, propriété 16-18 SocketHandle, propriété 30-6, SQL standard 15-11
SetAbort 50-5, 50-6, 50-10 30-8 SQL, analyseur 21-18
SetBrushStyle, méthode 7-8 sockets 4-11, 30-1–30-15 SQL, applications
SetComplete 50-5, 50-6, 50-10 acceptation des requêtes accès aux tables 20-2
SetComplete, méthode client 30-3 actions groupées 20-25
modules de données affectation d’hôtes 30-4 ajout d’enregistrements 18-26
MTS 14-20 description 30-3 contraintes de données 19-25,
SetData, méthode 19-21 écriture dans les 30-12 19-26
SetEvent, méthode 8-10 fourniture d’informations 30-4 édition de données 18-8
SetFields, méthode 18-28 gestion d’événements 30-8– insertion
SetFloatValue, méthode 38-9 30-10, 30-11, 30-13 d’enregistrements 18-9, 18-26
SetKey, méthode 18-9, 20-7 gestion des erreurs 30-9 localisation de données 20-6,
EditKey ou 20-9 implémentation des 20-9
SetLength, procédure 3-33 services 30-1–30-2, 30-7 suppression
SetMethodValue, méthode 38-9 lecture à partir de 30-11 d’enregistrements 20-18
SetOrdValue, méthode 38-9 lecture/écriture 30-11–30-15 tri de données 20-11
SetParams, méthode 25-22 réseau, adresses 30-3, 30-4 SQL, constructeur 21-7
SetPenStyle, méthode 7-7 Sorted, propriété 2-19, 26-14 SQL, instructions
SetRange, méthode 20-15, 20-16 source, ensembles de données SQL direct 13-9
SetRangeEnd, méthode 20-14 définition 20-22 SQL, propriété 21-6, 21-8
SetRange ou 20-15 sources de décision 27-10 modification 21-16
SetRangeStart, méthode 20-13 événements 27-10 SQL, requêtes 21-1
SetRange ou 20-15 propriétés 27-10 caractères d’espacement
SetStrValue, méthode 38-9 sources de données 12-13, 26-6– et 21-6
26-9 caractères spéciaux et 21-6
SetValue, méthode 38-9
ajout 26-6, 26-7 création 21-4, 21-7
SGBD 14-1
association aux ensembles de à l’exécution 21-7
SGBDR 12-3, 14-1 définition de paramètres
données 26-7
Shape, propriété 2-25 à l’exécution 21-11
attribution de nom 26-7
shift, état des touches 7-25 définition 26-6 définition des
ShortCut, propriété 5-22 liaison aux requêtes 21-11 instructions 21-6–21-9
ShortString 3-27 mise à jour 26-8 définition des
Show, méthode 5-6, 5-8 serveurs distants 17-9 paramètres 21-9–21-13
ShowAccelChar, propriété 2-23 souris ensembles de résultats 21-14,
ShowButtons, propriété 2-20 événements 7-24–7-27 21-17–21-19
ShowFocus, propriété 26-32 glisser-déplacer 6-1 curseurs et 21-17
ShowHint, propriété 2-24 sous-classement des contrôles mise à jour 21-19
ShowHints, propriété 26-35 Windows 31-4 obtention à l’exécution 21-14
ShowLines, propriété 2-20 sous-menus 5-22 exécution 21-13–21-15
ShowModal, méthode 5-6 Spacing, propriété 2-17 à partir de fichiers
ShowRoot, propriété 2-20 texte 21-8
SparseCols, propriété 27-10
signalement d’événements 8-10 multitables 21-16
SparseRows, propriété 27-10 optimisation 21-13, 21-17
signets 18-15–18-16 spécifications d’attributs
site Web (support Delphi) 1-3 préparation 21-15
bibliothèques de types 49-26 présentation 21-1–21-5
Size, propriété 19-16 SPX/IPX, protocole 17-8 serveurs distants 21-19
Smart Agents 28-2, 28-3–28-4 SQL 12-3 soumission
configuration 28-19–28-22 ADO 23-22 d’instructions 21-15
DDL 23-23, 23-29

Index I - 35
SQL, serveurs 12-3 support développeur 1-3 dénomination 20-3
contraintes 19-25 support technique 1-3 droits d’accès 20-5
SQL, standards 21-4 Suppression de modèles, boîte en lecture seule 20-5
serveurs distants 21-19 de dialogue 5-28 extraction de données 20-12,
SQLPASSTHRUMODE 13-10 suppressions en cascade 15-4 20-21, 21-1
squelettes 28-2, 28-3, 28-8 Supprimer les modèles, insertion
marshaling 28-3, 28-8 commande (menu d’enregistrements 18-25–
Standard, page de la palette des Concepteur) 5-26, 5-28 18-26, 18-28
composants 2-11 Supprimer, commande (menu Interbase 12-17
StartTransaction, méthode 13-7 Concepteurr) 5-25 maître/détail, relations 20-28–
State, propriété 2-18, 23-5 20-29
surcharge
colonnes de grille 26-19 mise à jour des données avec
méthodes 32-9, 37-3, 41-11
grilles 26-19, 26-22 des 25-24
Suspend, méthode 8-12
State, propriété interne ouverture 20-4
Sybase, pilote parcours des 20-5–20-9
ADO 23-15 déploiement 11-6
restructuration 13-11
StatusCode, propriété 29-16 synchronisateur à écriture spécification du type 20-3
STDVCL40.DLL 11-6 exclusive et à lecture suppression 20-18
Step, propriété 2-24 multiple 8-8 suppression
StepBy, méthode 2-24 précaution d’utilisation 8-8 d’enregistrements 20-18
StepIt, méthode 2-24 synchronisation des précaution 20-18
stockages de données 23-3 données 20-27 synchronisation 20-27
stored, directive 33-12 sur plusieurs fiches 26-7, 26-8 tri de données 20-10–20-12
StoreDefs, propriété 20-20 Synchronize, méthode 8-4 avec des index
StoredProcName, propriété 22-4 système d’aide 38-4 secondaires 20-10
StrByteType 3-31 boutons outils 5-38 vidage 20-18
Stretch, propriété 26-12 fichiers 38-4 tables ADO 12-17
StretchDraw, méthode 7-5, 36-3, mots clés 38-5 présentation 23-21
36-7 Système, page de la palette des utilisation 23-21
string, mot réservé 3-28 composants 2-11 tables en lecture seule 20-5
type par défaut 3-26 systèmes de gestion de bases de tables IB 12-17
VCL, types de propriété 3-28 données 14-1 tables imbriquées 12-16, 19-30–
Strings, propriété 2-35 relationnelles 12-3 19-31, 20-29
Structured Query systèmes hôtes à localisations client, ensembles de
Language 12-3 multiples 28-21 données 14-30
stThreadBlocking, fichiers linéaires 24-4
constante 30-10 T maître/détail, relations 14-30
stubs 28-2, 28-3, 28-8, 28-13, tables non indexées 18-26, 18-28
28-13–28-14 Table , balise HTML parcours 20-6
création 28-14 (<TABLE>) 29-20 tables Oracle 22-19
marshaling 28-3 table, composants 12-16, 20-2 tables problématiques 20-26
Style, propriété 2-19, 2-20, 2-25, TableAttributes, propriété 29-25 TableType, propriété 20-3
14-42 tableau, champs 19-29 TabOrder, propriété 2-13
boîtes à options 26-13 tableaux 33-3, 33-9 Tabs, propriété 2-22
boutons outils 5-36 sécurisés 49-25 TabStop, propriété 2-13
crayons 7-5 tableaux grilles 2-24 TAction 5-39
pinceaux 7-8 TableName, propriété 23-22 TActionLink 5-39
variants dessinés par le tables 12-15, 20-1 TActionList 5-39
propriétaire 6-13–6-14 affichage dans les grilles 26-19 TActiveXControl 48-2
StyleRule, propriété 14-42 ajout 20-2–20-4 TADOCommand 23-1, 23-24,
Styles, propriété 14-42 basées sur ADO 12-17 23-28, 23-30
StylesFile, propriété 14-43 changement de nom 20-18 TADOConnection 23-1, 23-3,
substitution des paramètres composants d’aide à la 23-4, 23-6, 23-11, 23-14, 23-20,
(SQL) 25-17, 25-22 décision et 27-3
23-21, 23-23, 23-24, 23-25
Subtotals, propriété 27-14 création 13-11, 13-16, 20-19
se connecter à un stockage de
Supplément, page de la palette définitions des champs et des
données 23-3
des composants 2-11 index 20-20
TADODataSet 13-14, 23-20

I-36 Guide du développeur


TADOQuery 13-14, 23-22, 23-29 TCustomControl 31-4 TDecisionGraph 27-2, 27-15
TADOStoredProc 13-14, 23-24 TCustomGrid 41-1, 41-2 instanciation 27-15
TADOTable 13-14, 23-21, 23-22 TCustomIniFile 2-38 TDecisionGrid 27-2, 27-12
TADTField 19-1 TCustomListBox 31-3 événements 27-14
Tag, propriété 19-16 TDatabase 17-1, 17-10 instanciation 27-12
TAny 28-14 DatabaseName, propriété, propriétés 27-14
création de types et 13-4 TDecisionPivot 27-2, 27-3, 27-11
structurés 28-16 instances temporaires 16-8, propriétés 27-11
TApplicationEvents 5-3 16-9, 17-2 TDecisionQuery
TArrayField 19-1 TDataSet 5-45, 18-2, 18-32 propriétés 27-7
TAutoIncField 19-1 TDataSetAction 5-45 TDecisionQuery,
TBatchMove TDataSetCancel 5-45 composant 27-5, 27-6
gestion des erreurs 20-26 TDataSetDelete 5-45 TDecisionSource 27-10
TBCDField 19-1 TDataSetEdit 5-45 événements 27-10
TBDEDataSet 18-31, 18-33 TDataSetFirst 5-45 propriétés 27-10
TBitmap 36-4 TDataSetInsert 5-45 TDefaultEditor 38-13
TBlobField 19-1 TDataSetLast 5-45 TDependency_object 4-8
TBlobStream 2-38 TDataSetNext 5-45 TDragObject 6-4
TBooleanField 19-1 TDataSetPost 5-45 TEditAction 5-44
TBrush 2-25 TDataSetPrior 5-45 TEditCopy 5-44
tbsCheck, constante 5-36 TDataSetProvider 14-19, 15-2 TEditCut 5-44
TBytesField 19-2 TDataSetTableProducer 29-26 TEditPaste 5-44
TCalendar 41-1 TDataSource 26-6–26-9 TEnumProperty, type 38-8
TCGIApplication 29-6 propriétés 26-6–26-8 termes du contrat de licence
TCGIRequest 29-6 TDateField 19-2, 19-18 logicielle 11-12
TCGIResponse 29-6 TDateTime, type 41-5 Terminate, méthode 8-6
TCharProperty, type 38-8 TDateTimeField 19-2, 19-18 Terminated, propriété 8-6
TClassProperty, type 38-8 TDBChart 12-15 test
TClientDataSet 24-1 TDBCheckBox 26-2, 26-16 composants 31-13, 43-6-43-7
applications linéaires 13-15 TDBComboBox 26-2, 26-13 connexions 16-5
TClientSocket 30-6 TDBCtrlGrid 26-2, 26-31–26-32 valeurs 33-7
TClientWinSocket 30-6 propriétés 26-32 TEvent 8-10
TColorProperty, type 38-8 TDBDataSet 18-31 Text, propriété 2-14, 2-20, 2-23
TComObject propriétés 18-32 texte 26-10, 26-11
agrégation 3-22 TDBEdit 26-2, 26-10 contrôles dessinés par le
TComponent 2-7, 2-10, 31-5 TDBGrid 26-2, 26-18 propriétaire 6-13
TComponentProperty, événements 26-30 copier, couper, coller 6-10
type 38-8 propriétés 26-24, 26-27 dans les contrôles 6-7
TDBGridColumns 26-19 dessin sur les canevas 7-25
TControl 2-11, 31-4, 34-5, 34-6
TDBImage 26-2, 26-12 imprimer 2-14
TCoolBand 2-18
TDBListBox 26-2, 26-13 internationalisation 10-9
TCoolBar 5-32 lecture de la droite vers la
TCorbaConnection 14-25 TDBLookupComboBox 26-2,
gauche 10-5
TCorbaDataModule 14-5 26-14–26-16
manipulation 6-7
TCorbaPrincipal 28-17 TDBLookupListBox 26-2, 26-14–
rechercher 2-14
TCP/IP 30-1 26-16 sélection 6-9, 6-9–6-10
applications TDBMemo 26-2, 26-10 statique 2-23
multiniveaux 14-10 TDBNavigator 18-11, 18-12, suppression 6-11
clients 30-6 26-2, 26-32–26-35 travail sur le 6-13
connexion au serveur TDBRadioGroup 26-2, 26-17 tronqué 26-10
d’applications 14-23 TDBRichEdit 26-11 texte, fichiers
protocole 17-8 TDBText 26-2, 26-9 exécution de requêtes à partir
serveurs 30-7 TDCOMConnection 14-23 de 21-8
serveurs d’applications TDecisionCube 27-5, 27-8–27-9 TextHeight, méthode 7-5, 36-3
et 14-14 événements 27-8 TextOut, méthode 7-5, 7-25,
TCurrencyField 19-2 TDecisionDrawState 27-14 36-3
TCustomContentProducer 29-18

Index I - 37
TextRect, méthode 7-5, 36-3 mise en mémoire cache 30-15 TMemIniFile 2-37
TextWidth, méthode 7-5, 36-3 objets distribués 8-13 TMemoField 19-2
TField 19-1 priorités 8-1, 8-3 TMemoryStream 2-38
ajout 19-1–19-5 redéfinition 8-11 TMessage 37-4, 37-6
événements 19-20 renvoi de valeurs 8-9 TMetafile 36-4
méthodes 19-20 sections critiques 8-7 TMethodProperty, type 38-8
propriétés 19-3, 19-14–19-19 serveur, sockets 30-12, 30-14– TMsg 5-5
exécution 19-16 30-15 TMTSDataModule 14-5
TFieldDataLink 42-5 serveurs de messagerie 8-12 TMultiReadExclusiveWriteSync
TFiler 3-41 serveurs en processus 8-14
hronizer 8-8
TFileStream 2-38 sessions de base de données
TNotifyEvent 34-8
E/S de fichier 3-41–3-45 et 13-5, 16-3
utilisation avec des sockets
TNumericField 19-2
TFloatField 19-2 TObject 2-7, 32-4
TFloatProperty, type 38-8 client 30-14
utilisation avec des sockets Top, propriété 2-12, 5-3, 5-33
TFontNameProperty, type 38-8 TopRow, propriété 2-24
TFontProperty, type 38-8 serveur 30-15
utilisation de listes 8-5 TOrdinalProperty, type 38-8
TForm touches accélératrices 5-21
VCL, thread 8-4
barres de défilement 2-15 TPageProducer 29-19
verrouillage des objets 8-7
TForm, composant 2-3 TPanel 5-31
threads de service 4-6
TFrame 5-14 TParameter 23-19, 23-31
threads en suspens 8-12
TGraphic 36-4 TPersistFormat, type 23-18
threads multiples
TGraphicControl 31-4, 40-2 tpHigher, constante 8-3
attente de 8-10
thread VCL principal 8-4 tpHighest, constante 8-3
threadvar 8-5
OnTerminate, événement 8-6 TPicture, type 36-4
THTMLTableAttributes 29-25
Thread, boîte d’état 8-15 tpIdle, constante 8-3
THTMLTableColumn 29-25
thread, fonction 8-4 tpLower, constante 8-3
TickMarks, propriété 2-15
thread, objets 8-2 tpLowest, constante 8-3
définition 8-2
TickStyle, propriété 2-15
TIcon 36-4 tpNormal, constante 8-3
limites 8-2
tiDirtyRead, constante 13-8 TPopupMenu 5-38
ThreadID, propriété 8-15 TPropertyAttributes 38-11
threads 8-1–8-15 TImage
dans les cadres 5-16 TPropertyEditor, classe 38-8
accès aux données,
TImageList 5-35 tpTimeCritical, constante 8-3
composants 8-5
arrêt 8-6, 8-12 TIniFile 2-37 TQuery 2-40, 21-1
TIntegerField 19-2 ajout 21-4
attente d’événements 8-10
TIntegerProperty, type 38-8, ensembles de données de
attente de 8-9
38-10 décision et 27-6
bibliothèques 8-14
blocage de l’exécution 8-7 TInterfacedObject 3-23 TQueryTableProducer 29-26
boucle des messages et 8-5, dérivation de 3-19 traduction 10-9
8-13 implémentation de traduction de chaînes de
client, sockets 30-12, 30-13– IUnknown 3-19 caractères 10-2, 10-8, 10-10
30-14 liaison dynamique 3-19 conversions sur 2 octets 10-3
COM 8-13 tiReadCommitted, traitement distribué des
coordination 8-4, 8-7–8-11 constante 13-8 données 14-2
CORBA 8-13 tiRepeatableRead, transactions 12-5, 13-5–13-10
création 8-11 constante 13-8 abandon 13-8
espace de processus 8-4 TISAPIApplication 29-5 applications
éviter les accès simultanés 8-7 TISAPIRequest 29-5 multiniveaux 14-29
exécution 8-11 TISAPIResponse 29-5 attributs
graphiques, objets 8-5 Title, propriété objets MTS 50-17, 50-19
identificateurs 8-15 grilles de données 26-25 changements non validés
initialisation 8-3 TKeyPressEvent, type 34-4 et 13-8
ISAPI/NSAPI, contrôles 13-6
TLabel 31-4
programmes 29-7, 29-23 démarrage 13-7
.TLB, fichiers 44-15, 49-37
libération 8-3 durée 13-7
TLIBIMP 44-16 implicites 13-6
limites du nombre de 8-11 TListBox 31-3 locales 13-10

I-38 Guide du développeur


mises à jour en mémoire cache TStrings 2-32–2-37 enregistrement de
et 25-2, 25-5 TStringStream 2-38 message 37-6
modules de données TTable 2-40, 20-1 gestionnaires
MTS 14-17 ensembles de données de d’événements 34-3
MTS 14-7, 14-29, 50-5, 50-7, décision et 27-6 mise en correspondance avec
50-12 TThread 8-2 les tables de base de
attributs 50-8 TThreadList 8-5, 8-7 données 20-25
contrôlées par le client 50-10 TTimeField 19-2, 19-18 objets CORBA 28-7
temporisations 50-11 TToolBar 5-32, 5-34 propriétés 33-2, 33-9, 38-9
niveaux d’isolement 13-8–13-9 TToolButton 5-32 types autorisés
support côté client 50-21 bibliothèques de types 49-24
TTypedComObject
support côté serveur 50-22 types caractère 3-25, 10-2
bibliothèques de types
utilisation des bases de types de champs 19-1, 19-2
nécessaires 44-14
données 13-7 conversion 19-20, 19-22
TUpdateAction, type 25-29
validation 13-7, 13-8 spécification 19-8
TUpdateKind, type 25-28
transactions implicites 13-6 surcharge 19-19
TUpdateSQL 21-19
transactions locales 13-10 types de données 19-8
événements 25-26–25-27
transfert d’enregistrements 43-2 mise en correspondance 20-25
TUpdateSQL, composant 25-13
TransIsolation, propriété 13-8 types définis par
turboboutons 2-17
Transliterate, propriété 19-16, affectation de glyphes 5-33 l’utilisateur 40-3
20-23 ajout aux barres d’outils 5-33– types ensemble 33-2
Transparent, propriété 2-23 5-34 types énumérés 33-2, 40-3
TReader 3-41 centrage 5-33 ajout aux bibliothèques de
TReferenceField 19-2 état initial, définition 5-33 types 48-12, 49-34
TRegistry 2-37 gestionnaires déclaration 7-12
TRegistryIniFile 2-38 d’événements 7-13 éditeur de bibliothèques de
TRegSvr 11-3, 44-16 groupe 5-34 types 49-18, 49-29
TRemoteDataModule 14-5 modes de fonctionnement 5-33 types simples 33-2
tri de données 20-10–20-12 pour les outils de dessin 7-13 autorisés dans les interfaces
avec des index utilisation comme CORBA 28-7
secondaires 20-10 bascules 5-34
triangles 7-12 TVarBytesField 19-2 U
tris avec différence majuscules/ TWebActionItem 29-8
minuscules 20-11, 24-8 TWebApplication 29-5 un à plusieurs, relations 20-27
try, mot réservé 36-7, 43-5 TWebRequest 29-5 unDeleted, constante 25-12
TScrollBox 2-15 TWebResponse 29-5, 29-8 Unicode standard
TSearchRec 3-37 chaînes 3-26
TWinCGIRequest 29-6
TServerClientThread 30-12 Unicode, caractères 10-3
TWinCGIResponse 29-6
TServerClientWinSocket 30-7, chaînes 3-28, 3-29
TWinControl 10-8, 31-4, 34-5
30-8 UniDirectional, propriété 21-17
TWindowAction 5-45
TServerSocket 30-7 unions
TWindowArrange 5-45 éditeur de bibliothèques de
TServerWinSocket 30-7 TWindowCascade 5-45 types 49-21, 49-30
TService_object 4-8 TWindowClose 5-45 unité CorbaInit 28-13
TSession 16-1, 16-2, 17-10 TWindowMinimizeAll 5-45
ajout 16-4, 16-17
unité stub-et-squelette 28-7,
TWindowTileHorizontal 5-45 28-8, 28-13
TSessionList 16-1 TWindowTileVertical 5-45
TSessions, composant 16-1 unités
TWinSocketStream 2-38, 30-13 accès depuis d’autres
TSetElementProperty, type 38-8 TWordField 19-2 unités 2-7
TSetProperty, type 38-8 TWriter 3-41 ajout de composants 31-11
TSmallintField 19-2 type ASP 51-3 éditeurs de propriétés 38-8
TSocketConnection 14-23 type, mot réservé 7-13 existantes
TStoredProc 22-4 typeinfo 49-2 ajout d’un composant 31-11
TStream 2-38 types inclusion de paquets 9-4
TStringField 19-2, 19-18 Automation 47-9 Unlock, méthode 8-7
TStringList 2-32–2-37 Char 10-2 UnlockList, méthode 8-7
TStringProperty, type 38-8 définis par l’utilisateur 40-3 UnPrepare, méthode 21-15

Index I - 39
UnregisterPooled,
procédure 14-8
V verrouillage des objets
imbrication des appels 8-7
UnRegisterTypeLib, fonction $V, directive de threads 8-7
désinstallation des compilation 3-35 verrous 20-5
bibliothèques de valeurs 33-2 verrous exclusifs 20-5
désinstallation 44-15 booléennes 33-2, 33-11, 42-4 VertScrollBar 2-15
UPDATE, instructions 21-14, default, propriété 33-11 vidéo analogique 7-34
21-15, 25-13 données par défaut 19-25, violations d’accès
Update, méthode 26-12 chaînes 3-32
actions 5-44 null 18-28, 20-14 violations de clé 20-26
UpdateCalendar, méthode 42-4 portées et 20-14 virtual
UpdateMode, propriété 15-8 propriété par défaut 33-8 directive 32-9
UpdateObject, propriété 18-34, redéfinition 39-2, 39-3 tables de méthode 32-9
25-13 test 33-7 virtuelles
transtypage 25-20 valeurs de référence 26-21 méthodes 32-9, 35-4
UpdateRecordTypes, valeurs des axes 27-17 éditeurs de propriétés 38-9–
propriété 18-34, 25-10, 25-11 valeurs littérales 19-25 38-10
UpdatesPending, valeurs logiques 26-2, 26-16 propriétés comme 33-2
propriété 18-34, 25-4 valeurs récapitulatives 27-21 visibilité 2-8
UpdateSQL, éditeur 25-16 agrégats maintenus 24-13 Visible, propriété 19-16
UpdateStatus, propriété 18-34, graphes de décision 27-17 barres d’outils 5-38, 5-39
25-12 références croisées 27-3 barres multiples 5-38
UpdateTarget, méthode 5-44 validation menus 5-29
URI d’enregistrements 26-5 VisibleButtons, propriété 26-33,
URL ou 29-2 grilles de données 26-28 26-34, 26-35
URL 29-2 validation des saisies de VisibleColCount, propriété 2-24
bibliothèques javascript 14-36, données 19-20 VisibleRowCount,
14-37 validation des transactions 13-7, propriété 2-24
connexions Web 14-24 13-8 VisiBroker ORB 14-15
IP, adresses 30-4 Value, propriété 19-21 VisualSpeller Control 11-3
noms d’hôte 30-4 ValueChecked, propriété 26-17 volets 2-22
URI ou 29-2 Values, propriété 26-18 ajout de turboboutons 5-33
Web, navigateurs 29-3 ValueUnchecked, aligné sur le haut de la
URL, propriété 29-14 propriété 26-17 fiche 5-32
USEPACKAGE, macro 9-8 var, mot réservé vtable 44-5
uses, clause 2-7 gestionnaires vtables
éviter les références d’événements 34-4 pointeur d’interface COM 44-5
circulaires 5-2 variables 21-7 vues arborescentes 2-20
inclusion de paquets 9-4 déclaration
modules de données exemple 2-8
et objets 2-8–2-9
W
accès depuis des fiches 2-39
usInserted, constante 25-12 variables d’environnement WaitFor, méthode 8-9, 8-10
usModified, constante 25-12 CORBA 28-18 WaitForData, méthode 30-13
usUnmodified, constante 25-12 variables locales aux WantReturns, propriété 2-14
utilisateur, interfaces 12-13– threads 8-5 WantTabs, propriété 2-14
12-18 OnTerminate, événement 8-6 contrôles d’édition de texte
enregistrement unique 12-13 variants 18-17, 18-18, 19-23 formaté 26-11
enregistrements CORBA 28-14 mémo, champs 26-10
multiples 12-14 TAny 28-14 .WAV, fichiers 7-34
isolement 12-8 VCL 31-1–31-2 $WEAKPACKAGEUNIT,
Utiliser unité, commande présentation 2-1 directive de compilation 9-12
(Fichier) 5-2 propriétés de type chaîne 3-28 Web
Utiliser unité, menu thread principal 8-4 base de données 14-32–14-44
Fichier 2-40 VCL30, paquet 9-1, 9-10 connexions 14-24
Utiliser, référentiel d’objets 2-42 PENWIN.DLL 9-13 Web, applications
déploiement 11-7
objet 29-7

I-40 Guide du développeur


Web, déploiement Windows Wrapable, propriété 5-36
applications boîtes de dialogue wrError, constante 8-10
multiniveaux 14-34 standard 43-1, 43-2 Write By Reference, propriété
Web, modules 29-6–29-7, 29-9 création 43-2 de bibliothèque de types 49-11
ajout de sessions de base de exécution 43-5 Write, méthode
données 29-23 contextes de TFileStream 3-43
DLL et, précaution 29-7 périphériques 31-7, 36-1 write, méthode 33-7
Web, navigateurs 29-3 contrôles, sous-classement 31-4 write, mot réservé 33-9, 40-4
URL 29-3 fonctions API 31-4, 36-1 wrSignaled, constante 8-10
Web, pages 29-3 GDI (Graphics Device wrTimeout, constante 8-10
Web, répartiteur 29-7, 29-9– InterfaceI) 7-1
messages 5-4
29-10
gestion des requêtes 29-8, 29-13 support de la largeur du X
sélection d’éléments crayon 7-7 $X, directive de
d’action 29-11, 29-12 Windows NT compilation 3-36
Web, serveurs 14-34 débogage d’applications Xerox Network System
client, requêtes et 29-4 serveur Web 29-27 (XNS) 30-1
WebDispatch, propriété 14-39 WM_APP, constante 37-6 XML 14-38–14-40, 23-18
WebPageItems, propriété 14-41 WM_KEYDOWN, message 42-9 XMLBroker, propriété 14-42
WideChar 3-25, 3-28 WM_LBUTTONBUTTON, XMLDataSetField,
Width, propriété 2-23, 5-3, 26-19, message 42-9 propriété 14-42
26-25 WM_MBUTTONDOWN,
message 42-9
crayons 7-5, 7-6
TScreen 11-9 WM_PAINT, messages 7-2
Y
Widtht, propriété 2-12 WM_RBUTTONDOWN, Year, propriété 41-5
Win 3.1, page de la palette des message 42-9
composants 2-11 WM_SIZE, message 41-4 Z
Win32, page de la palette des WndProc, méthode 37-4
composants 2-11 WordWrap, propriété 2-14, 6-7, -Z, directive de
Win-CGI, programmes 4-12, 39-1 compilation 9-13
29-4, 29-5, 29-6 mémo, champs 26-11 zéro terminal
création 29-6 wParam, paramètre 37-2 chaînes étendues 3-28
débogage 29-31 wrAbandoned, constante 8-10
INI, fichiers 29-6 Wrap, propriété 5-36

Index I - 41
I-42 Guide du développeur

You might also like